Script runit di Sidekiq troppo fragile: **discourse:www-data** **+ forced** **-L log/sidekiq.log** **causa crash di 1 secondo**

Ciao team,

segnalo una modalità di fallimento nella configurazione ufficiale Docker/runit che può terminare silenziosamente Sidekiq (e quindi i job AI/in background) senza alcuna ricostruzione o aggiornamento.

Ambiente

  • Installazione ufficiale di Discourse Docker (container standard + servizi runit).
  • Nessuna ricostruzione/aggiornamento immediatamente prima dell’inizio del problema.
  • Plugin Discourse AI abilitato, ma l’AI ha smesso di rispondere.

Sintomi

  • L’AI risulta abilitata nell’interfaccia di amministrazione, ma non compaiono risposte dall’AI.
  • I job in background (AI/embedding/risposta automatica) sembrano bloccati.
  • sv status sidekiq mostra che Sidekiq muore ripetutamente subito dopo l’avvio:
down: sidekiq: 1s, normally up, want up
  • L’avvio manuale di Sidekiq funziona correttamente, quindi l’applicazione è a posto:
bundle exec sidekiq -C config/sidekiq.yml
# rimane attivo, si connette a Redis, elabora i job

Cosa abbiamo scoperto

Lo script runit predefinito era:

exec chpst -u discourse:www-data \
  bash -lc 'cd /var/www/discourse && ... bundle exec sidekiq -e production -L log/sidekiq.log'

Due punti di fragilità:

  1. Gruppo primario www-data Nel mio container, i percorsi tipicamente scrivibili sono di proprietà di discourse:discourse. Qualsiasi deriva in tmp/pids o nei percorsi condivisi può far uscire Sidekiq durante l’avvio quando eseguito sotto www-data, anche se l’avvio manuale come discourse funziona.
  2. Scrittura forzata -L log/sidekiq.log nei log condivisi Il percorso del log è un collegamento simbolico a /shared/log/rails/sidekiq.log. Se quel file/directory viene ricreato con proprietà/permessi diversi, Sidekiq può uscire immediatamente prima di produrre log utili.

Trigger correlato: logrotate fallisce quotidianamente

Separately, logrotate falliva ogni giorno con:

error: skipping "...\log" because parent directory has insecure permissions
Set "su" directive in config file ...

La causa erano i permessi standard Debian/Ubuntu:

  • /var/log è root:adm con 0775 (scrivibile dal gruppo).
  • logrotate rifiuta la rotazione a meno che non venga impostata una direttiva su globale. Questo è il comportamento previsto a monte.

Nel momento in cui il job giornaliero di logrotate è fallito, ha anche ricreato i file sotto /shared/log/rails/ (incluso sidekiq.log), che probabilmente ha interagito con la registrazione forzata tramite -L e ha contribuito al ciclo di crash di Sidekiq “1s”.

Correzione (nessuna ricostruzione necessaria)

  1. Correggere logrotate in modo che smetta di toccare i log condivisi in stato di errore Aggiungere una direttiva su globale:
# /etc/logrotate.conf (inizio)
su root adm

Dopodiché, logrotate -v esce con 0 e non segnala più permessi non sicuri della directory padre.

  1. Sostituire lo script runit di Sidekiq con uno predefinito più robusto Passare a discourse:discourse e al sidekiq.yml standard, e non forzare -L log/sidekiq.log, rende Sidekiq stabile:
#!/bin/bash
exec 2>&1
cd /var/www/discourse

mkdir -p tmp/pids
chown discourse:discourse tmp/pids || true

exec chpst -u discourse:discourse \
  bash -lc 'cd /var/www/discourse && rm -f tmp/pids/sidekiq*.pid; exec bundle exec sidekiq -C config/sidekiq.yml'

Dopo questo:

  • sv status sidekiq rimane in esecuzione:
  • I job AI/in background riprendono.

Richiesta / suggerimento

Potremmo considerare di rendere il servizio Sidekiq ufficiale Docker/runit più robusto per impostazione predefinita?

Ad esempio:

  • Eseguire Sidekiq sotto discourse:discourse (corrispondente alla proprietà tipica all’interno del container).
  • Preferire bundle exec sidekiq -C config/sidekiq.yml.
  • Evitare di forzare un file di log condiviso tramite -L log/sidekiq.log, o renderlo resiliente alla deriva dei permessi di logrotate/volume condiviso.

Anche una nota nella documentazione (“se Sidekiq mostra down: 1s ma l’avvio manuale funziona, controllare /etc/service/sidekiq/run ed evitare la registrazione condivisa forzata”) aiuterebbe molto gli self-hoster.

Sono disponibile a fornire ulteriori log se necessario. Grazie!

Dove lo stai trovando? Sidekiq viene avviato tramite il master di unicorn per conservare memoria. Non vedo questo codice affatto in discourse_docker. Sembra che tu stia usando una configurazione molto vecchia?

Ciao, permettimi di ribadire questo basandomi strettamente sui fatti di runtime dal container Docker ufficiale.

Cosa sto vedendo nel container in esecuzione (fatti)

Questa è un’installazione Docker ufficiale con runit (flusso di lavoro standard del lanciatore /var/discourse; nessuna ricostruzione immediatamente prima dell’incidente). All’interno del container:

  1. Esiste un servizio Sidekiq runit ed è quello supervisionato
ls -l /etc/service/sidekiq/run
sv status sidekiq

Output durante l’incidente:

down: sidekiq: 1s, normally up, want up
  1. L’avvio manuale di Sidekiq funziona
cd /var/www/discourse
sudo -u discourse bundle exec sidekiq -C config/sidekiq.yml

Questo rimane attivo, si connette a Redis ed elabora i job.

  1. La sola modifica di /etc/service/sidekiq/run (nessuna ricostruzione) risolve immediatamente il crash loop Sostituito /etc/service/sidekiq/run con:
#!/bin/bash
exec 2>&1
cd /var/www/discourse
mkdir -p tmp/pids
chown discourse:discourse tmp/pids || true
exec chpst -u discourse:discourse \
  bash -lc 'cd /var/www/discourse && rm -f tmp/pids/sidekiq*.pid; exec bundle exec sidekiq -C config/sidekiq.yml'

Dopo di che:

sv status sidekiq
run: sidekiq: (pid <PID>) <SECONDS>s

Quindi Sidekiq non viene avviato tramite Unicorn master in questa immagine; è un servizio runit il cui script di runtime può entrare in un ciclo di crash.

Perché potresti non vedere il codice esatto in

discourse_docker

Concordo sul fatto che il testo letterale potrebbe non essere nel repository perché /etc/service/sidekiq/run è un artefatto di runtime generato/iniettato durante la build/boot dell’immagine, non necessariamente un file verbatim in discourse_docker. Ma è il servizio supervisionato attivo in questa immagine ufficiale, come mostrato sopra.

Cosa ha innescato la fragilità (fatti + minima inferenza)

  • Abbiamo anche osservato fallimenti giornalieri di logrotate a causa delle autorizzazioni Debian standard: /var/log = root:adm 0775, quindi logrotate ha rifiutato la rotazione fino all’aggiunta di global su root adm.
  • Quando logrotate falliva, ricreava file sotto /shared/log/rails/, incluso sidekiq.log.
  • Lo script runit predefinito in questa immagine utilizzava discourse:www-data e forzava -L log/sidekiq.log in /shared/log, il che rende Sidekiq molto sensibile alla deriva delle autorizzazioni del volume condiviso e può causare un’uscita immediata prima che vengano generati log utili.

Richiesta / proposta

Dato quanto sopra, potremmo considerare di rafforzare il servizio Sidekiq Docker/runit predefinito?

Impostazioni predefinite suggerite:

  • eseguire come discourse:discourse (corrisponde alla proprietà tipica all’interno del container),
  • avviare tramite bundle exec sidekiq -C config/sidekiq.yml,
  • evitare di forzare un -L log/sidekiq.log condiviso (o renderlo resiliente).

Ciò impedirebbe il ciclo di crash silenzioso down: 1s che interrompe tutti i job in background/AI.

Sono disponibile a testare qualsiasi branch/commit che mi indicherai.

Ancora… sono confuso su dove stai prendendo la tua immagine:

image

Questa è l’immagine ufficiale.

Questa è una ricerca per la parola sidekiq nel discourse docker ufficiale.

https://github.com/search?q=repo%3Adiscourse%2Fdiscourse_docker%20sidekiq&type=code

Ci sono 3 risultati… niente su un’unità runit. È gestito tramite unicorn.

Ciao, grazie, quello screenshot aiuta a chiarire il layout.

Sono d’accordo che nell’immagine ufficiale attuale Sidekiq non è un servizio runit separato (nessun /etc/service/sidekiq/). Viene avviato dalla catena di avvio del servizio runit di unicorn, che corrisponde al tuo elenco /etc/service.

Il mio rapporto riguarda ancora una modalità di errore a runtime di quel percorso di avvio di Sidekiq, indipendentemente dal fatto che risieda in un’unità runit autonoma o all’interno di unicorn/run:

Fatti dal runtime sul mio VPS (Docker ufficiale, nessun rebuild/aggiornamento subito prima dell’incidente):

  1. I processi in background si sono interrotti e le risposte AI si sono interrotte.

  2. Sidekiq è entrato in un loop di crash immediato (giù: 1s) quando è stato avviato dal supervisore/catena di avvio del container.

  3. L’avvio manuale come discourse tramite bundle exec sidekiq -C config/sidekiq.yml è rimasto attivo ed ha elaborato i job, quindi l’app/redis erano a posto.

  4. Allo stesso tempo, i fallimenti di logrotate hanno causato la ricreazione di /shared/log/rails/sidekiq.log (e percorsi correlati) con permessi diversi; dopo aver stabilizzato il comando di avvio di Sidekiq (eseguito come discourse:discourse, usa sidekiq.yml, evita di forzare shared -L sidekiq.log), il loop di crash si è interrotto immediatamente.

Quindi il file letterale /etc/service/sidekiq/run potrebbe non esistere in questa immagine — d’accordo — ma il passaggio di avvio di Sidekiq incorporato nel servizio runit di unicorn è fragile rispetto alle permessi del volume condiviso/deriva di logrotate e può silenziosamente terminare Sidekiq senza un rebuild. Questo è il problema principale.

Suggerimento: si prega di considerare di rafforzare l’avvio di Sidekiq nello script runit di unicorn ufficiale (o dove viene generato):

  • Esegui Sidekiq sotto discourse:discourse,

  • Preferisci bundle exec sidekiq -C config/sidekiq.yml,

  • Evita di forzare un -L log/sidekiq.log condiviso (o rendilo resiliente).