Configurazione di distribuzione di Discourse secondo l'opinione di MKJ

Ho gestito un forum Discourse con una quantità sostanziale di contenuti e molte immagini negli ultimi anni. Maker Forums ha oltre 100 GB di immagini e più di 400.000 post, di cui una quantità significativa è stata importata, principalmente da Google+, e il resto è stato creato sul sito. Questo post descrive gli elementi di come ho alla fine configurato Maker Forums e, successivamente, alcune altre istanze Discourse. È ciò che avrei voluto sapere quando ho iniziato, e che ho usato per aiutare altri a evitare alcune delle stesse insidie per le loro istanze Discourse.

Tempo di ampliare il pubblico.

:warning: Attenzione: Se non ti senti a tuo agio a lavorare come amministratore di sistema Linux, questa guida probabilmente non fa per te. Potrei nemmeno essere consapevole di tutti i modi in cui presuppone conoscenze su Linux. Se leggere questo ti sembra illuminante, potresti essere il pubblico target. Se ti sembra confuso, probabilmente non sei il pubblico target. Se ti sembra un lavoro, valuta di pagare CDCK o @pfaffman per gestire Discourse per te; sanno quello che stanno facendo. Oppure inizia con un sito gratuito discourse.group e poi paga per ciò in cui cresci. :warning:

:warning: Come se non bastasse: ho più esperienza con Linux che con Discourse. Le mie opinioni non sono coperte da garanzia. Se seguire i miei consigli causa la rottura di qualcosa di tuo (il tuo forum Discourse, il tuo sistema host o il tuo cuore), ti restano entrambi i pezzi, con tutti i bordi taglienti. Non ho intenzione di fornire alcun tipo di supporto per i contenuti di questo post. :warning:

Ho l’intenzione (ma non prometto) di mantenere questo documento aggiornato con le mie pratiche relative alle istanze Discourse che partecipiamo a mantenere. È scritto in forma di consiglio, ma intendo principalmente come consiglio a me stesso e a qualsiasi amministratore che erediti distribuzioni Discourse di cui mi sono occupato. Altrimenti, dovresti considerarlo come un punto di partenza per la tua propria ricerca per determinare come desideri distribuire Discourse.

Configurazione del sistema

Usa un OS derivato da CentOS o Ubuntu LTS. Qualsiasi cosa che supporti Docker può probabilmente essere fatta funzionare, ma ho usato questi due.

Docker

Sono un utente Fedora. Sono stato il primo Fedora Project Lead a Red Hat, e preferirei molto di più eseguire Discourse su Podman perché ritengo che il suo modello di sicurezza sia preferibile a quello di Docker. Tuttavia, le distribuzioni Discourse sono supportate solo su Docker, e sarai un vero pioniere se provi a eseguire su qualsiasi altra cosa. (Potrebbe, un giorno, funzionare con Podman usando podman-compose se docker-compose venisse mai supportato.)

Ora che Docker supporta cgroups v2, puoi installare le build ufficiali di Docker su un sistema derivato da CentOS:

dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
dnf install --allowerasing docker-ce docker-ce-cli

(Includi --allowerasing a causa di un conflitto con podman, runc e buildah che potrebbero essere già installati; devono essere rimossi per installare docker.)

systemctl enable --now docker

Ho testato questo con AlmaLinux 9.

Sicurezza

Questa sezione non ha davvero a che fare con Discourse in sé, ma fa parte della mia normale pratica di sicurezza. Non consentire l’accesso shell solo con password a qualsiasi sistema sulla rete, incluso una VM che esegue Discourse. Configura l’accesso basato su SSH usando una chiave SSH crittografata con passphrase e configura il server ssh sulla tua VM per non consentire l’accesso con password.

laptop$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/.../.ssh/id_rsa):
Enter passphrase (empty for no passphrase): UNA FRASE LUNGA
Enter same passphrase again: UNA FRASE LUNGA
Your identification has been saved in .ssh/id_rsa
Your public key has been saved in .ssh/id_rsa.pub

Le distribuzioni Linux sono normalmente configurate per ricordare la passphrase in memoria, quindi devi digitarla solo una volta per avvio. Windows non è così comodo; potresti considerare l’uso di Pageant con PuTTY per fare lo stesso.

Prima, valida che l’SSH in arrivo funzioni senza password. Solo dopo aver fatto questo, sul server, modifica il file /etc/ssh/sshd_config e trova la riga PasswordAuthentication. Impostala su no per disabilitare l’accesso con password in arrivo.

PasswordAuthentication no

Firewall

Dovrai lasciare le porte 80 e 443 generalmente aperte per letsencrypt per generare e rinnovare i tuoi certificati SSL, anche prima che il tuo Discourse sia aperto al pubblico.

Se stai usando firewalld, questi comandi realizzeranno questo:

firewall-cmd --add-service http --add-service https --zone public
firewall-cmd --runtime-to-permanent

Dispositivo e file system separati

Rendi /var/discourse/shared un dispositivo separato con il proprio file system, con 20 GB di spazio più almeno il doppio dello spazio di cui hai bisogno per le immagini; aggiungi più spazio se userai prometheus. Se il dispositivo sarà facile da espandere in seguito (come LVM o qualsiasi storage a blocchi cloud come AWS elastic block storage) puoi monitorarlo e farlo crescere come necessario; altrimenti sii generoso all’inizio. Se stai usando un dispositivo di storage a blocchi di rete, non mettere una tabella delle partizioni su di esso. Usarlo senza una tabella delle partizioni renderà più facile l’espansione; non dovrai modificare una tabella delle partizioni. In molti casi potrai espandere senza alcun downtime del sistema.

Su Maker Forums, questo è un dispositivo di storage a blocchi di rete attaccato alla VM su cui Maker Forums è in esecuzione. Su un altro forum Discourse, è un Volume di Storage a Blocchi Digital Ocean. In Amazon, questo sarebbe AWS Elastic Block Storage. Sul mio sistema di test che esegue una VM KVM sotto libvirt su Fedora, è un volume LVM sull’host Fedora esportato alla VM AlmaLinux come disco virtuale. In ogni caso, potevo creare una nuova VM, copiare i file chiave su di essa, fermare la vecchia VM, attaccare il volume /var/discourse/shared alla nuova VM e tornare operativo in pochi minuti. Questo rende gli aggiornamenti del sistema operativo sulla VM a rischio relativamente basso.

Assicurati di iniziare con almeno 25 GB sul file system root della tua VM, non includendo spazio per /var/discourse/shared. Questo sarà usato per tutti i container docker, e il launcher discourse fallirà se meno di 5 GB sono liberi in qualsiasi momento. Vuoi anche molto spazio disponibile per gli aggiornamenti di sistema. Se non hai abbastanza spazio su disco, è difficile recuperare da questo.

Nella configurazione del sito, imposta force_https ma presta attenzione agli avvertimenti. Configuralo in test, prima di rendere pubblico un sito Discourse. Nota che anche con force_https hai bisogno della porta 80 aperta, sia per reindirizzare a SSL sulla porta 443 che per rinnovare il tuo certificato SSL letsencrypt. (Tuttavia, se stai usando cloudflare, usa la sua funzione invece; si riferisce che non è compatibile con force_https in Discourse.)

Configurazione del kernel

Redis (uno dei componenti chiave su cui Discourse è costruito) raccomanda fortemente di disabilitare le huge pages trasparenti quando si usa la persistenza su disco (che Discourse fa), e permetto anche l’overcommit della memoria.

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
echo 'vm.overcommit_memory=1' > /etc/sysctl.d/90-vm_overcommit_memory.conf
sysctl --system

Installazione di Discourse

Mentre l’installazione predefinita è un singolo container, questo rende ogni aggiornamento, raccomandato mensilmente, tipicamente un downtime di 10-15 minuti se fatto dalla riga di comando, che è necessario per alcuni aggiornamenti, inclusi quelli che aggiornano gli strumenti su cui Discourse è costruito, per sicurezza o nuove funzionalità, o quando l’aggiornamento live dall’interfaccia utente fallisce per qualsiasi motivo. Puoi ridurre quel downtime nella pratica con l’installazione a due container.

Installazione a due container

Inizia la configurazione con due container.

./discourse-setup --two-container --skip-rebuild
${EDITOR:-nano} containers/data.yml
./launcher rebuild data
# la mia preferenza è usare app.yml ma potresti restare con web_only.yml, leggi il testo
mv containers/web_only.yml containers/app.yml
${EDITOR:-nano} containers/app.yml
./launcher rebuild app

Questo rende il downtime di sistema necessario ogni pochi mesi molto breve, raramente percettibile; molti utenti non se ne accorgeranno affatto se non cliccano o scorrono oltre i contenuti durante il downtime. Questo rende più facile applicare la maggior parte degli aggiornamenti di sicurezza; è solo un’interruzione piuttosto che circa 15 minuti di ricostruzione di tutto. Il seguente processo funziona per la maggior parte degli aggiornamenti e tipicamente dà circa 30-90 secondi di downtime, dipendendo principalmente dalle prestazioni del sistema host e dall’insieme di plugin installati.

cd /var/discourse
git pull
./launcher bootstrap app
./launcher destroy app && ./launcher start app
./launcher cleanup

Non ritardare tra le invocazioni bootstrap e destroy/start. Raramente (forse una o due volte all’anno nella pratica), le migrazioni del database fatte vicino alla fine della fase bootstrap causeranno errori più o meno seri da presentare agli utenti dell’app, a causa del codice più vecchio che accede al database aggiornato.

Questo significa che quando aggiorni Discourse, devi anche controllare se aggiornare anche il container dati, ma questo è raramente richiesto (tipicamente aspetta una o due volte all’anno). A seconda dei contenuti dei tuoi container dati e app e della velocità del sistema, questo risulterà tipicamente in un downtime tra 5 e 20 minuti.

cd /var/discourse
git pull
./launcher stop app
./launcher rebuild data
./launcher rebuild app

Per saperne di più su quando aggiornare il container dati, vedi:

(Nelle mie distribuzioni personali, ho scelto personalmente di chiamare il container web_only app sia perché è più facile da digitare che perché rende la maggior parte delle istruzioni più facili da seguire. Questo è non standard ma continuo ad apprezzare la facilità d’uso. Tuttavia, è stato lavoro extra, e funziona per me perché so cosa sta succedendo. Se questo ti sembra male, resta con il web_only predefinito invece per una distribuzione multi-container.)

Nota che in qualche punto in futuro, Docker potrebbe costringerti a fare una migrazione a una nuova configurazione per connettere i tuoi container:

Se hai 4 GB o più di memoria, o più CPU, leggi i consigli a:

Programma di aggiornamento

Monitora il tag release-notes (Clicca su release-notes e clicca sulla campana in alto a destra; uso “Watching First Post”) e/o aggiungi https://meta.discourse.org/tag/release-notes.rss al tuo feed RSS per sapere quando ci sono rilasci. Leggi le note di rilascio prima di aggiornare. Se c’è un cambiamento nel database, le note di rilascio lo menzioneranno. Evidenzieranno anche i rilasci che contengono aggiornamenti di sicurezza. Leggi tutte le note di rilascio anche se salti l’aggiornamento effettivo a una versione; se non leggi le note di rilascio per un rilascio che aggiorna il database, potresti perdere le istruzioni di aggiornamento del database nelle note di rilascio che non hai letto.

Posta

La posta è ancora uno dei modi chiave per mantenere le connessioni tra le persone. Configura la posta in uscita e in entrata per far lavorare la posta per te. Se hai problemi vedi:

Continua a cercare

Maker Forums ha visto visitatori occasionali che sono stati via per lunghi periodi prima di tornare. Di default, Discourse smette di inviare email di digest dopo un anno. Considera di impostare suppress_digest_email_after_days su qualcosa di più lungo dei 365 giorni predefiniti se vuoi incoraggiare i visitatori occasionali a tornare quando vedono qualcosa di nuovo e interessante. L’ho reso sostanzialmente più lungo per Maker Forums per supportare i visitatori occasionali che si tengono aggiornati. Leggere le email di digest è un modo valido per “nascere” su un forum, e non sai mai quando qualcosa potrebbe accendere l’interesse di qualcuno nel contribuire.

Similmente, di default, gli utenti non privilegiati che non hanno interagito molto (livello di fiducia 0 senza post) sono eventualmente eliminati dopo 730 giorni di non accesso. Imposta “pulisci utenti inattivi dopo giorni” a 0 per disabilitare l’eliminazione degli utenti, se vuoi che siano in grado di nascere leggendo email di digest indefinitamente.

Considera di aggiungere il plugin yearly review che una volta all’anno, genererà un post come 2020: The Year in Review e alla fine lo invierà via email ai tuoi utenti inattivi che potrebbe incoraggiarli a rinnovare la partecipazione.

Container ricevitore di posta

Configura un terzo container come ricevitore di posta. Assicura l’elaborazione dei rimbalzi, rende la tua elaborazione dei rimbalzi indipendente dal provider di posta in uscita e ti dà l’opzione di risposta via email.

Assicurati di avere SPF configurato per fidarti del tuo mittente email; minimamente, una politica come v=spf1 +mx -all se invii e ricevi attraverso lo stesso MX, ma più specifico potrebbe essere più fidato come protezione contro lo spam. Considera anche DKIM.

Se usi lo stesso nome host per ricevere email, dovresti davvero terminare SSL fuori dal tuo container come per una “pagina offline” (vedi sotto) e dovrai mappare i tuoi certificati certbot nel container e riavviare il container dopo aver eseguito certbot.

Termina le connessioni SSL degli utenti fuori dal container

Ci sono due scelte per terminare SSL fuori dal container, entrambe delle quali portano vantaggi sostanziali rispetto a terminare dentro il container. Configura una di esse dopo aver completato con successo discourse-setup e aver bootstrapato il tuo forum.

Nginx esterno

Usa nginx in esecuzione sul sistema host, piuttosto che solo in un container, sia per ospitare una pagina di manutenzione, che per supportare la registrazione degli indirizzi IPv6 se il tuo host ha supporto IPv6. (Altrimenti, tutte le connessioni IPv6 saranno registrate come provenienti da un indirizzo RFC1918 interno associato alla tua interfaccia di rete virtuale docker locale.) Questa configurazione presenterà una pagina di manutenzione temporanea durante la maggior parte delle operazioni di manutenzione che alla fine reindirizzerà alla pagina che un utente stava guardando.

Nota che le istruzioni su quella pagina (attualmente) suggeriscono di installare un pacchetto chiamato letsencrypt ma ora è normalmente chiamato certbot invece. Se segui le istruzioni su quella pagina per usare --certonly, non avrai bisogno del plugin nginx per certbot, ma installare il plugin nginx è un altro meccanismo. Su derivati CentOS:

dnf config-manager --set-enabled crb
dnf install epel-release
dnf install certbot python3-certbot-nginx
systemctl enable --now certbot-renew.timer

Assicurati che certbot riavvii nginx e il container ricevitore di posta in modo da non finire con browser o email che bloccano il traffico con il tuo sito a causa della continuazione dell’uso di un certificato vecchio e scaduto.

# systemctl edit certbot-renew

Per un sistema senza un ricevitore di posta, ho aggiunto le due righe:

[Service]
ExecStartPost=/bin/systemctl reload nginx

Su un sistema dove sto usando un container ricevitore di posta separato che condivide anche il certificato dal sistema:

[Service]
ExecStartPost=/bin/systemctl reload nginx
ExecStartPost=/bin/sh -c 'cd /var/discourse && ./launcher restart mail-receiver'

Se stai usando SELinux, i container Ubuntu non sono configurati per etichettare il file nginx.http.sock con httpd_sys_content_t per consentire a nginx esterno di accedervi. Hai due scelte.

La prima è eseguire nginx in modalità permissiva, rimuovendo la protezione SELinux per esso: semanage permissive -a httpd_t

Tuttavia, questo rimuove la protezione SELinux da ciò che è probabilmente il servizio più rilevante! Per mantenere SELinux abilitato, dovrai permettere a nginx di accedere alle pagine di errore e passare dal proxying su un socket di dominio unix a una porta (che è alcuni µs più lento, ma non dovrebbe essere percettibile dai tuoi utenti).

Prima, esegui questi comandi per permettere a nginx di accedere alle pagine di errore:

semanage fcontext -a -t httpd_sys_content_t /var/www
restorecon -R -v /var/www

Poi nel tuo app.yaml, commenta o rimuovi - "templates/web.socketed.template.yml", esponi la porta 80 come una porta diversa sulla macchina locale e ricostruisci il container.

expose:
  - "8008:80"   # http

Non usare https qui — hai terminato SSL in nginx esterno, e l’intestazione X-Forwarded-Proto dice a Discourse che la richiesta è arrivata via https. Assicurati che la porta 8008 (o qualsiasi altra porta che hai scelto) non sia esposta pubblicamente dalle tue impostazioni del firewall.

Poi esegui questo comando per permettere a nginx di connettersi alla rete al container:

setsebool -P httpd_can_network_connect 1

Poi modifica la tua configurazione nginx esterna da proxying via nginx.http.sock a http://127.0.0.1:8008 (o la tua porta scelta) e cancella l’intestazione predefinita Connection: close, in modo che nginx esterno non debba stabilire una nuova connessione IP per ogni richiesta.

...
  location / {
    proxy_pass http://127.0.0.1:8008;
    proxy_set_header Host $http_host;
    proxy_http_version 1.1;
    # Disable default "Connection: close"
    proxy_set_header "Connection" "";
...

Rimuovere web.socketed.template.yml ha anche rimosso l’invocazione real_ip, quindi aggiungila di nuovo. Assicurati che l’intervallo di indirizzi IP che usi abbia senso; il default di Docker è usare lo spazio degli indirizzi RFC1918 172.16* che non sono instradati su internet pubblico per politica. Aggiungi al tuo file app.yml qualcosa come questo nella sezione run, selezionando uno o più degli spazi degli indirizzi RFC1918 o qualsiasi altra cosa appropriata per la tua distribuzione:

run:
  - file:
     path: /etc/nginx/conf.d/outlets/server/real-ip-recursive.conf
     chmod: 644
     contents: |
       real_ip_recursive on;
  - file:
     path: /etc/nginx/conf.d/outlets/server/real-ip-header.conf
     chmod: 644
     contents: |
       real_ip_header X-Forwarded-For;
  - file:
     path: /etc/nginx/conf.d/outlets/server/set-real-ip-from.conf
     chmod: 644
     contents: |
       set_real_ip_from 192.168.0.0/16;
       set_real_ip_from 172.16.0.0/12;
       set_real_ip_from 10.0.0.0/8;

Questo è richiesto per il rate limiting per funzionare correttamente, così come per attribuire indirizzi IP di registrazione e ultimo uso per gli utenti.

Per ulteriori informazioni:

Servizio esterno

Non ho configurato Fastly o Cloudflare davanti a Discourse, ma altri l’hanno fatto, e a differenza di nginx esterno in esecuzione sull’host, possono permetterti di servire una pagina di manutenzione mentre il sistema host è completamente giù, come quando si riavvia durante un aggiornamento di sistema sul tuo host. Se questo vale la pena per te, ecco come farlo:

Non affrettarti con gli upload S3

Sii molto sicuro che vuoi sempre usare S3 (o equivalente) per le immagini caricate prima di abilitare enable_s3_uploads durante la configurazione, o migrare ad esso in seguito. Sii consapevole che l’uso di S3 (s3_endpoint) con il suo CDN associato (s3_cdn_url) per le immagini risulterà anche nel servire javascript via quel CDN. La migrazione da S3 allo storage locale non è supportata e non ci sono piani concreti per implementarlo in questo momento. È una “porta a senso unico” che non può nemmeno essere annullata da un backup e ripristino completo. Se usi S3 o simile, non usare Digital Ocean Spaces invece di S3. Ci sono riferimenti qui su meta a non essere affidabile.

Ho spostato il mio sito a servire immagini attraverso Digital Ocean Spaces e il suo CDN associato presto, e ho dovuto scrivere centinaia di righe di codice personalizzato per migrare di nuovo allo storage locale, causando danni minori alla mia istanza Discourse nel processo, a causa della “porta a senso unica” non essere ben compresa.

Per ulteriori informazioni:

Non hai bisogno di abilitare gli upload S3 per usare un CDN per il tuo Discourse. Considera di usare un CDN indipendente (es. Cloudflare, CloudFront, Fastly, GCS CDN) davanti a un Discourse che gestisce le proprie immagini. È la mia comprensione indiretta che l’avvertimento su Cloudflare non essere raccomandato è dovuto a “Rocket Loader” che modifica JavaScript; e che in questo momento, finché non usi “Rocket Loader” funziona correttamente.

Impostazioni Discourse per la moderazione

Su qualsiasi sito dove la moderazione è attiva, considera fortemente la configurazione enable_whispers che permette ai moderatori e agli amministratori di parlare di un argomento in linea. Inoltre, i moderatori di categoria hanno ricevuto più capacità nelle versioni recenti di Discourse. Vale la pena essere consapevoli di enable_category_group_moderation se hai esperti in argomenti diversi con le loro categorie, o se hai categorie funzionalmente separate come per il supporto.

La geolocalizzazione può essere utile quando si cerca di capire se un account è legittimo.

La funzione Discourse Templates è davvero utile per i moderatori. Ti permette di collaborare su risposte comuni. Abbiamo alcune decine su Maker Forums. Ha più funzionalità del plugin precedente “Canned Responses” che sostituisce.

La funzione User Notes aiuterà i moderatori a condividere note sugli utenti. Puoi metterle in uso per cose come:

  • “Tieni d’occhio questo utente, potrebbero essere malintenzionati perché …”
  • “Mentre questo comportamento sembra sospetto, ho validato che questo è un utente legittimo da …”
  • “Sto già avendo una conversazione con questo utente per affrontare le preoccupazioni, altri moderatori non devono accumulare.”

Accessibilità delle informazioni

Il plugin Discourse Solved non solo marca i problemi risolti in modo che i visitatori del sito possano identificarli più facilmente, ma capisco che potrebbe anche prioritizzare i risultati di ricerca google.

Le informazioni pubbliche sono più accessibili delle informazioni private. Su Maker Forums, la nostra FAQ scoraggia fortemente i messaggi personali e ricorda a tutti che i messaggi personali non sono veramente privati. Tuttavia, di default, gli utenti potrebbero vedere il messaggio:

Hai risposto a utente 3 volte, sapevi che potevi inviare loro un messaggio personale invece?

Se vuoi davvero incoraggiare gli utenti a andare ai messaggi personali, suggerisco di andare su Admin → Customize → Text e cambiare il template get_a_room per correggere la virgola splice.

Se, come Maker Forums, vuoi mantenere la conversazione pubblica per beneficiare tutti, Admin → Settings → Other → get_a_room_threshold può essere impostato più alto, come 1000000.

Similmente, se hai un forum che fornisce aiuto, il max_replies_in_first_day default 10 potrebbe spingere nuovi utenti in una conversazione che chiede aiuto nei messaggi personali quando esauriscono il loro budget di risposte. Considera di aumentare questa impostazione per evitare di spingere le conversazioni nei messaggi personali.

Connetti gli utenti, costruisci una comunità

Alcuni plugin possono aiutare a connettere gli utenti tra loro.

Se il tuo forum non ha troppi utenti simultanei, considera il plugin Who’s Online per dare alle persone più senso di connessione. Potresti voler limitare la visualizzazione agli utenti connessi, possibilmente solo quelli che hanno raggiunto almeno il livello di fiducia 1. Puoi usarlo solo per aggiungere presenza flair (whos_online_avatar_indicator) agli avatar impostando whose_online_minimum_display molto alto e whos_online_hide_below_minimum_display true. Questo può essere utile per i forum di supporto per supportare e incoraggiare domande e risposte rapide mentre si aiutano gli utenti a risolvere i problemi.

Tuttavia, un senso di presenza può tagliare in entrambe le direzioni. Un utente che è online in un orario diverso dalla maggior parte degli utenti del forum potrebbe sentirsi solo, o il forum potrebbe sembrare una “città fantasma” per loro.

Se hai utenti in molti paesi e vuoi che abbiano indizi su quando gli altri sono più probabilmente disponibili, considera il plugin National Flags, e incoraggia gli utenti a impostare una bandiera nazionale nel loro profilo.

Uno difficile è la traduzione. Sarebbe comodo aiutare le persone a comunicare quando non parlano la stessa lingua, ma attualmente (al momento della scrittura) non ci sono servizi di traduzione con livelli di servizio gratuiti. Se scegli di pagare per servizi di traduzione, puoi abilitare la traduzione con il plugin Discourse Translator.

Backup

Per i file di sistema, considera di fare backup almeno di:

  • /var/discourse/containers (per i dettagli di configurazione di discourse)
  • /var/www (per le pagine di errore)
  • /etc/ssl (per la configurazione di letsencrypt, per evitare di dover bootstrapare certbot come parte del ripristino di un backup; altrimenti devi commentare la porzione SSL della tua configurazione nginx mentre stai bootstrapando; questo funziona solo se mantieni i backup recenti perché i certificati hanno validità breve)
  • /etc/systemd/system/backup-uploads.service (per fare backup delle immagini a S3)
  • /usr/local/bin/mc (minio-client come strumento di backup immagini, se scegli di usarlo)
  • /root/.mc (configurazione per backup immagini con minio-client)
  • /root/.ssh (autenticazione sessione SSH in arrivo)

Alcuni di questi file potresti fare backup controllandoli in Git e spingendoli da qualche parte fuori sito. Se i file che controlli in Git includono segreti (come password del database), definitely non spingerli a un repository pubblico. In alternativa, potresti scriptare la copia fuori dal sistema e il controllo in Git su un sistema di supervisione che controlli. Scripta questo sufficientemente frequentemente per mantenere i tuoi backup di /etc/ssl freschi.

L’obiettivo è avere backup sia in caso di disastro che per avere registro di cambiamenti in caso di errore.

Un’alternativa migliore per la maggior parte di questi file è mantenere le copie canoniche altrove, e usare uno strumento come Ansible per mantenere la configurazione sul sistema, che rende altrettanto facile aggiornare dopo un backup. Ma se stai per fare questo, probabilmente l’hai capito senza che te lo dicessi!

Configurazione Discourse per backup

  • Fai backup delle miniature con include_thumbnails_in_backups. Un ripristino senza miniature richiede molto tempo per rigenerarle. Se il tuo sito non ha molte grafiche, le miniature occupano spazio insignificante. Se il tuo sito è ricco di grafiche, rigenerare le miniature potrebbe richiedere giorni. Mentre le miniature vengono rigenerate, le notifiche email saranno disabilitate. In ogni caso, non ha senso omettere le miniature dai backup.

  • Non includere immagini nei backup se hai molte immagini. Questo renderà i backup lenti e ingombranti. Fallo backup separatamente. Se fai backup delle immagini dopo il backup del database, i tuoi backup saranno coerenti.

  • Organizza per i backup di andare fuori sito in qualche modo.

Questa pagina mostra come configurare backup del database a S3 o qualcosa come S3:

Fai backup con restic

Configura discourse per fare backup al file system, poi fai backup dal file system a un target di backup remoto con restic. Ecco una ricetta di esempio.

# dnf install restic
# mkdir /var/restic
# mkdir /opt/backup
# cd /opt/backup
# cat > backup <<EOF
#!/usr/bin/bash

set -e
. /opt/backup/backup-config

restic --cache-dir=/var/restic \
	backup \
	/etc \
	/root \
	/var/discourse \
	/opt/backup \
	--exclude /var/discourse/shared/data/postgres_data

restic --cache-dir=/var/restic forget \
	--prune --keep-hourly 24 --keep-daily 7 --keep-monthly 3
EOF
# chmod +x backup

Questi dettagli varieranno a seconda del target restic. Non tutti utilizzano le variabili di ambiente AWS, quindi leggi la documentazione di restic.

# cat > backup-config <<EOF
### Questi dettagli dipendono dal target restic che configuri, quindi modificali
export AWS_ACCESS_KEY_ID=la-tua-chiave
export AWS_SECRET_ACCESS_KEY=la-tua-chiave-secreta
export RESTIC_PASSWORD_FILE=/root/restic-password
export RESTIC_REPOSITORY=vedi-la-documentazione-restic
EOF
# {$EDITOR:-nano} /root/restic-password

Dovrai conservare una copia di ciò che inserisci in /root/restic-password, altrimenti non potrai leggere i backup! Usa il tuo vault per le password.

Infine, crea alcuni servizi, inizializza il repository restic e imposta un timer in modo che i backup abbiano inizio.

# cat > /etc/systemd/system/backup.service <<EOF
[Unit]
Description=Backup su target remoto
Wants=network-online.target
After=network-online.target

[Service]
Type=oneshot
StandardOutput=file:/var/log/backup.out
StandardError=file:/var/log/backup.err
WorkingDirectory=/var/discourse
ExecStart=/opt/backup/backup

[Install]
WantedBy=multi-user.target
EOF
# cat > /etc/systemd/system/backup.timer <<EOF
[Unit]
Description=Backup di sistema regolari

[Timer]
Persistent=true
OnCalendar=00/4:30:00
Unit=backup.service

[Install]
WantedBy=timers.target
EOF

# systemctl daemon-reload
# . /opt/backup/backup-config
# restic init
# /opt/backup/backup
# systemctl enable backup.timer

Dopo questo processo, dovresti essere in grado di vedere che hai creato il tuo backup iniziale.

# restic snapshots

Ho utilizzato con successo i backup restic realizzati in questo modo per spostare un server Discourse da un sistema a un altro utilizzando il comando restic restore.

Backup immagine streaming con minio

Come alternativa a Restic, mentre i backup del database possono essere archiviati su S3, non esiste un backup immagine S3 separato dal servizio delle immagini da S3. Un’alternativa è utilizzare minio-client per copiare le immagini su qualsiasi storage simile a S3. Questo può includere molti target simili a S3, tra cui S3 e minio, ma non DigitalOcean Spaces perché è costruito sopra il filesystem Ceph che non implementa l’API ListObjectsV2 nello stesso modo in cui lo fa S3.

In S3, crea un bucket che blocchi l’accesso pubblico (PermissionsBlock public access è il modo più semplice per ottenere questo risultato in AWS).

Installa minio-client (mc) in qualche modo. Ecco un modo.

curl https://dl.min.io/client/mc/release/linux-amd64/mc > /usr/local/bin/mc && chmod +x /usr/local/bin/mc

Configura minio-client con un alias chiamato backup utilizzando un comando simile a questo:

# mc alias set backup https://s3.amazonaws.com ACCESSKEY SECRETKEY --api S3v4
# mc mirror /var/discourse/shared/standalone/uploads backup/UPLOADS-BACKUP-BUCKET

Quindi crea un servizio /etc/systemd/system/backup-uploads.service come questo

[Unit]
Description=Sincronizzazione backup remoto quasi in tempo reale degli upload di discourse
After=network.target
StartLimitIntervalSec=0

[Service]
Type=simple
Restart=always
RestartSec=600
User=root
ExecStart=/usr/local/bin/mc mirror --overwrite -a --watch /var/discourse/shared/app/uploads backup/UPLOADS-BACKUP-BUCKET

[Install]
WantedBy=multi-user.target

Nota che UPLOADS-BACKUP-BUCKET qui dovrebbe essere un bucket diverso dal s3_backup_bucket in cui configuri Discourse per caricare i backup del database. Inoltre, nota che il percorso sarà /var/discourse/shared/web_only/uploads se utilizzi la distribuzione multi-container standard.

# systemctl enable backup-uploads
# systemctl start backup-uploads
# journalctl -fu backup-uploads

Carica un’immagine di test e assicurati di vedere le righe per il backup riuscito delle immagini originali e ottimizzate. Control-C uscirà dalla modalità follow in journalctl.

Recupero

Non ho mai dovuto testare questo piano fino a questo momento. Questo riepilogo potrebbe omettere qualcosa.

  • Ripristina tutti i file backup in generale
  • Avvia nginx (ora la tua pagina di manutenzione sarà visibile)
  • Esegui una distribuzione normale di Discourse utilizzando i file ripristinati in /var/discourse/containers
  • Installa minio-client in /usr/local/bin/mc se non lo hai ripristinato dai backup
  • Se non hai fatto il backup di /root/mc, configura l’alias di backup # mc alias set backup https://s3.amazonaws.com ACCESSKEY SECRETKEY --api S3v4
  • # mc cp backup/UPLOADS-BACKUP-BUCKET /var/discourse/shared/app/uploads
  • Ripristina il backup del database più recente; ti consiglio di Restore a backup from the command line
  • Solo dopo aver confermato che il sito è operativo, riconfigura il backup degli upload su S3 come documentato sopra.

Backup postgresql streaming

In futuro, potrei creare, testare e fornire una configurazione per abilitare l’uso di archiviazione WAL continua per trasmettere backup Postgres quasi istantanei con minio-client utilizzando il comando archive-command in postgresql, simile ai backup upload streaming.

Monitoraggio delle prestazioni

Ci sono almeno due approcci per il monitoraggio delle prestazioni.

Container Prometheus

Configura prometheus, mettendo i log di prometheus in /var/discourse/shared/prometheus se lo stai eseguendo sullo stesso sistema. I file di Prometheus possono crescere molto, e non vuoi che riempiano il filesystem root; probabilmente vuoi anche portarli con te se ti sposti su un sistema host più recente (sia aggiornando a una VM più grande che a una VM con un’installazione di sistema operativo più recente).

Se distribuischi prometheus sul sistema Discourse (o ovunque else su internet pubblico), configura la sicurezza davanti ad esso. Installato in quel modo, un’opzione sarebbe la configurazione nginx come questa:

  location /prometheus/ {
    auth_basic "Prometheus";
    auth_basic_user_file /etc/nginx/prometheus-htpasswd;
    proxy_pass http://localhost:9090/;
  }

Sysstat

Se Prometheus è troppo, considera l’uso di sysstat invece.

  • dnf install sysstat (o apt install sysstat su debian e derivati)
  • systemctl enable --now sysstat
  • systemctl enable --now sysstat-collect.timer
  • systemctl enable --now sysstat-summary.timer
  • systemctl edit sysstat-collect.timer e cambia OnCalendar=*:00/10 in OnCalendar=*:00/2
  • Se /etc/default/sysstat esiste, cambia false in true

Dopo questo, il comando sar può dirti se stai esaurendo le risorse di tanto in tanto.

Altre Risorse

Ecco una discussione complementare (e più compatta) sull’uso di Discourse internamente come forma primaria di comunicazione interna.

54 Mi Piace

Ciao, grazie per questa guida

Quali sono attualmente la CPU (core) e la RAM?
Quali sono le impostazioni attuali:

  db_shared_buffers: "xGB"
  db_work_mem: "xMB"
  UNICORN_WORKERS:

2 vCPU (L5640 Xeon), 4 GB di RAM — ma grazie all’importazione massiccia, abbiamo contenuti per utente simultaneo superiori rispetto a un Discourse tipico costruito interamente in modo organico. Raramente abbiamo più di 5 utenti simultanei.

Attualmente non ho impostato db_work_mem in data.yml, ma sembra che sia impostato a 10 MB in /etc/postgresql/13/main/postgresql.conf. Nel mio file data.yml ho impostato db_shared_buffers: "768MB", ma ora vedo che in /etc/postgresql/13/main/postgresql.conf nel mio contenitore dati c’è scritto shared_buffers = 512MB, il che mi sorprende. Apparentemente non ho ricreato il mio contenitore dati dopo l’ultima modifica. :roll_eyes: Ho apportato la modifica di configurazione prima di aggiungere Prometheus (che utilizza più memoria), quindi prima di cambiare quella impostazione probabilmente sposterò Prometheus su un’altra istanza del server.

Nell’app, ho impostato UNICORN_WORKERS: 4

1 Mi Piace

Grazie @mcdanlj per tutte queste chicche. Qualche consiglio sulla manutenzione periodica/pianificata? come riavvii settimanali/mensili? o monitoraggio su/giù con riavvio automatico? Altra manutenzione periodica manuale o automatizzata?

1 Mi Piace

@jaffadog Tieni d’occhio il tag release-notes (è la campana in alto a destra; io uso “Watching First Post”) e/o aggiungi https://meta.discourse.org/tag/release-notes.rss al tuo feed RSS per sapere quando ci sono rilasci. Questo avviene tipicamente mensilmente per l’app, il che copre il riavvio mensile. Non riavvio più frequentemente secondo un programma. Inoltre:

Se usi letsencrypt e mail_receiver, probabilmente dovresti configurarlo per riavviare mail_receiver dopo aver ottenuto un nuovo certificato.

Questo è tutto ciò che mi viene in mente al momento. Grazie per aver chiesto, e ho incorporato come seguire release-notes e maggiori dettagli sul riavvio nel testo principale. Penso che ora sia tutto completamente coperto nel post originale.

Ho aumentato le istruzioni di aggiornamento per recuperare gli aggiornamenti in /var/discourse al fine di cambiare la versione dell’immagine di base, perché guardando Update base image for polkit vulnerability mi sono reso conto che non essere espliciti su questo passaggio potrebbe essere fuorviante. In precedenza lo consideravo una di quelle cose che fanno parte della documentazione di base. Lo script launcher contiene un riferimento specifico alla versione dell’immagine di base e finché non esegui git pull costruirai su un’immagine di base più vecchia e non eseguirai ciò che è stato testato. (Cerca image= in cima al file.)

1 Mi Piace

È peggio di così: è configurato per impostazione predefinita per eliminare gli account di livello 0 inattivi dopo un certo periodo. Nel mio caso, non era affatto quello che volevo! Controlla “pulisci utenti inattivi dopo giorni” e impostalo a zero, o a un numero molto grande.

2 Mi Piace

Ho esaminato la questione, ma per quanto ho capito significa che non hanno mai risposto al flusso “conferma il tuo indirizzo email” e quindi non avrebbero ricevuto email in ogni caso. Non credo che “inattivo” qui significhi “non accedere al sito”, ma vorrei sapere se mi sbaglio.

No, “inattivo” non significa attivo=falso qui.

Significa utenti a cui si applicano tutte le seguenti condizioni:

  • livello di fiducia 0
  • nessun post
  • non amministratore, non moderatore
  • ultima visualizzazione più di X giorni fa.

E sì, quella dicitura è in effetti confusionaria, sebbene l’impostazione lo spieghi in parte (“livello di fiducia 0 senza alcun post”).

8 Mi Piace

In realtà stavo confondendo impostazioni diverse e avevo in mente “pulisci utenti in staging non utilizzati dopo giorni”. Avevo già impostato “pulisci utenti inattivi dopo giorni” a zero in Maker Forums molto tempo fa, e non l’avevo menzionato qui; devo essermelo perso nel mio audit delle impostazioni del sito modificate quando cercavo cose che potessero essere di interesse generale. Grazie a entrambi, @Ed_S e @RGJ! Ho aggiornato il post con un altro paragrafo sull’abilitazione del lurking. :smiling_face:

4 Mi Piace

Ciao @mcdanlj grazie per l’ottima intuizione che condividi qui. Per quanto riguarda un’installazione a 2 container, sono confuso su come ciò offra un vantaggio nel ridurre i tempi di inattività durante gli aggiornamenti. Sulla mia installazione di prova, un aggiornamento tramite l’interfaccia GUI /admin/upgrade#/upgrade/all richiede diversi minuti come descrivi, ma il sito rimane operativo per gli utenti durante tutto il processo.

Quando si deve ricostruire dalla riga di comando, l’installazione di due container consente di avviare la nuova immagine mentre quella vecchia continua a funzionare.

2 Mi Piace

Come al solito, @pfaffman è più veloce di me e sa il fatto suo. :smiley:

Non faccio mai aggiornamenti dalla GUI. In questo modo, ogni volta che aggiorno, includerò tutti gli aggiornamenti di sicurezza del sistema nei container sottostanti su cui Discourse è in esecuzione. Ciò non significa che sia male usare la GUI, né è una raccomandazione per tutti gli altri. Gli aggiornamenti della GUI hanno tempi di inattività leggermente inferiori (il riavvio del container web interrompe momentaneamente il servizio, il che è uno dei diversi motivi per considerare questo insieme a nginx esterno), quindi è un compromesso e io ho preso la strada meno battuta.

Un’installazione a container singolo ha tempi di inattività più lunghi più spesso, se si aggiorna regolarmente per gli aggiornamenti di sicurezza e per gli occasionali aggiornamenti della versione del database, poiché Discourse sfrutta le nuove funzionalità di Postgresql. Senza rivedere dati effettivi, la mia sensazione è che ci sia un motivo per ricostruire questo 3-4 volte all’anno. Se tale quantità di tempi di inattività è accettabile dal tuo punto di vista, non c’è molto motivo di affrontare la complessità del deployment a 2 container.

4 Mi Piace

È molto gentile, ma si tratta delle tue opinioni. :wink:

Neanche io. Tranne con il mio dashboard, che ha un sacco di cose in più nel container (come Ansible, e non ricordo esattamente cosa), e se qualcuno sta eseguendo un aggiornamento usando dashboard.literatecomputing.com e poi distruggo quel container, la sua ricostruzione viene interrotta, il che è potenzialmente problematico. Quindi ho fatto alcuni aggiornamenti di docker_manager ultimamente, e sono piuttosto eleganti.

Non proprio. È almeno per lo più vero che se c’è una nuova immagine di base, docker_manager ti obbligherà a ottenerla (almeno, ci proverà).

È più o meno giusto. A titolo informativo, e non lo consiglio, ma ho interagito con un sacco di persone che non hanno fatto aggiornamenti per anni senza alcun problema.

1 Mi Piace

Sì, non c’è dubbio che gli aggiornamenti in-container siano ben implementati!

Sì, era quello che intendevo. :tada:

2 Mi Piace

Ciao di nuovo, grazie a entrambi per le vostre intuizioni. Quindi sto ancora cercando di decidere cosa farò per il mio setup di produzione. Capisco in teoria perché un’installazione a 2 container porterebbe a meno tempi di inattività. Ma sto ancora vedendo pochissimi tempi di inattività con il meccanismo di aggiornamento della GUI. L’ho appena cronometrato, ho avuto un aggiornamento di docker-manager e Discourse era 22 commit indietro. L’intero processo ha richiesto meno di 5 minuti e durante l’intero processo il forum era pienamente operativo. Certo, questa volta non ci sono stati aggiornamenti di PostgreSQL, ma se fosse stato necessario aggiornare anche quello, allora avrei avuto il massimo tempo di inattività anche con il metodo a 2 container, corretto? Quindi, se ho capito bene, il metodo a 2 container riduce i tempi di inattività solo quando è necessario un accesso SSH e una ricostruzione del container dell’app a causa di una modifica della configurazione (aggiunta/rimozione di plugin, ecc.)? Non prevedo frequenti modifiche alla mia configurazione di produzione, quindi non vedo alcuna potenziale riduzione dei tempi di inattività nel mio caso. O devo anche accedere via SSH e ricostruire i container per applicare certi tipi di aggiornamenti di funzionalità/sicurezza?

Va bene.

Sì, hai il tempo di inattività completo ogni volta che aggiorni postgresql (ogni anno o due, suppongo, più gli aggiornamenti di sicurezza a PostgreSQL stesso, anche se non sono stati così frequenti).

Prima di cambiare, ho avuto un paio di fallimenti durante gli aggiornamenti della GUI, ma non ricordo più i dettagli; storia antica.

Gli aggiornamenti della GUI non aggiornano l’immagine di base, quindi tutti gli aggiornamenti di sicurezza nel software fornito, come l’elaborazione delle immagini, non verranno applicati lì.

Mi piace ricompilare rapidamente il container dell’app come mia modalità normale per consumare gli aggiornamenti di sicurezza del container dell’app quando disponibili, in modo da non rimandare mai un tempo di inattività di 10 minuti per ricompilare tutto. Ecco perché git pull nel launcher fa parte della prima parte dell’applicazione di ogni aggiornamento; in modo che se l’immagine di base con cose come i programmi di elaborazione delle immagini è stata aggiornata, vengano applicati senza che io debba nemmeno pensare di chiedere se ci sono aggiornamenti di sicurezza da applicare. :smiling_face:

Ma alla fine, io personalmente trovo l’approccio a due container più semplice per me, e assolutamente non sto cercando di convincere altre persone a farlo. Se non ti sembra più semplice in base alla tua conoscenza ed esperienza, non farlo solo perché la mia guida personale e opinabile lo identifica come utile in un certo contesto. :grin:

2 Mi Piace

Capito! :wink: Anch’io tendo ad avere opinioni forti sui dettagli di implementazione tecnologica, ma apprezzo la tua prospettiva aggiuntiva.

Hmm, quindi è ancora così?

In ogni caso, nella mia esperienza con i forum tradizionali installati senza containerizzazione su uno stack LAMP/LEMP, il tipico vettore di vulnerabilità/attacco che porta a siti web compromessi nel mondo reale è quasi sempre nel codice dell’applicazione web o in uno dei suoi framework di sviluppo web. Quindi tendo ad avere un maggiore senso di urgenza verso gli aggiornamenti del codebase di Discourse, che sembrano essere gestiti dalla GUI, quindi immagino che mi stia orientando in quella direzione.


A proposito, in tema di downtime, una rapida menzione per Clear Linux: ho iniziato a testarlo a causa delle sue ottimizzazioni di basso livello nel puro calcolo numerico per cercare di risparmiare qualche ora sul mio enorme processo di importazione del forum. Potrebbero esserci effettivamente alcuni miglioramenti di velocità in quel caso, ma in generale, santo cielo, è follemente veloce nel riavvio, specialmente come guest KVM. Sul livello più economico di VPS posso riavviare e accedere nuovamente via SSH in poco più di 5 secondi. Quindi non vedo l’ora di usarlo quando ci saranno importanti aggiornamenti del sistema operativo host.

Sì. Ma un paio di volte all’anno devi eseguire una ricostruzione dalla riga di comando perché qualche componente nel container è stato aggiornato. E poi avrai 5-15 minuti di inattività. Eseguo quasi esclusivamente aggiornamenti dalla riga di comando (tranne sul mio dashboard dove potrei aggiornare solo il plugin del dashboard più volte al giorno) e ho un bel po’ di clienti per i quali eseguo quegli aggiornamenti dalla riga di comando quando sono richiesti (presumibilmente stanno eseguendo aggiornamenti tramite l’interfaccia web, ma spesso non lo fanno).

2 Mi Piace

La dashboard di Discourse notifica specificamente questo tipo di aggiornamento richiesto? O dovrei tenere d’occhio le PSA di Debian?