Sidekiq runitスクリプトがもろすぎる: **discourse:www-data** **+ forced** **-L log/sidekiq.log** **で1秒クラッシュが発生

チームの皆さん、

公式のDocker/runit設定で、再構築やアップグレードなしにSidekiq(ひいてはAI/バックグラウンドジョブ)がサイレントに停止する障害モードを報告します。

環境

  • 公式Discourse Dockerインストール(標準コンテナ + runitサービス)。
  • 問題発生直前の再構築/アップグレードなし。
  • Discourse AIプラグインが有効になっているが、AIが応答しなくなった。

症状

  • 管理UIではAIが有効に見えるが、AIの応答が表示されない。
  • バックグラウンドジョブ(AI/埋め込み/自動応答)がスタックしているように見える。
  • sv status sidekiqは、Sidekiqが起動直後に繰り返し停止していることを示す:
down: sidekiq: 1s, normally up, want up
  • Sidekiqを手動で起動すると正常に動作するため、アプリケーション自体は問題ない:
bundle exec sidekiq -C config/sidekiq.yml
# 起動し続け、Redisに接続し、ジョブを処理する

判明したこと

デフォルトのrunitスクリプトは次のとおりでした:

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

2つの脆弱な点:

  1. プライマリグループ www-data 私のコンテナでは、通常の書き込み可能なパスはdiscourse:discourseによって所有されています。tmp/pidsや共有パスのいずれかのドリフトにより、手動でdiscourseとして起動した場合は機能するにもかかわらず、www-dataの下で起動したSidekiqが起動時に終了する可能性があります。
  2. 強制された -L log/sidekiq.log 共有ログへの書き込み ログパスは/shared/log/rails/sidekiq.logへのシンボリックリンクです。そのファイル/ディレクトリが異なる所有権/パーミッションで再作成された場合、Sidekiqは有用なログを生成する前に即座に終了する可能性があります。

関連するトリガー: logrotateの毎日の失敗

別途、logrotateが毎日次のエラーで失敗していました:

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

原因は標準のDebian/Ubuntuパーミッションでした:

  • /var/log は root:adm で 0775 (グループ書き込み可能) です。
  • logrotateは、グローバルのsuディレクティブが設定されていない限り、ローテーションを拒否します。これはアップストリームの予期された動作です。

日々のlogrotateジョブが失敗した瞬間に、/shared/log/rails/(sidekiq.logを含む)内のファイルも再作成され、強制された-Lロギングと相互作用し、Sidekiqの「1秒クラッシュ」ループの一因となった可能性が高いです。

修正(再構築不要)

  1. logrotateが共有ログに触れるのを停止するように修正する グローバルなsuディレクティブを追加します:
# /etc/logrotate.conf (先頭)
su root adm

その後、logrotate -vは0で終了し、安全でない親パーミッションのエラーを報告しなくなります。

  1. Sidekiq runitスクリプトをより堅牢なデフォルトに置き換える discourse:discourseに切り替え、標準のsidekiq.ymlを使用し、-L log/sidekiq.logを強制しないことで、Sidekiqは安定します:
#!/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'

これを実行した後:

  • sv status sidekiqrunのままになります。
  • AI/バックグラウンドジョブが再開されます。

リクエスト/提案

公式のDocker/runit Sidekiqサービスをデフォルトでより堅牢にすることを検討していただけないでしょうか?

例えば:

  • Sidekiqをコンテナ内の通常の所有権と一致するdiscourse:discourseの下で実行する。
  • bundle exec sidekiq -C config/sidekiq.ymlを優先する。
  • -L log/sidekiq.logを介した共有ログファイルの強制を避けるか、logrotate/共有ボリュームのパーミッションのドリフトに対して耐性を持たせる。

ドキュメントに「Sidekiqがdown: 1sを示すが手動起動が機能する場合は、/etc/service/sidekiq/runを確認し、強制的な共有ロギングを避けてください」といった注記があるだけでも、セルフホスターにとって非常に役立ちます。

さらにログが必要な場合は喜んで提供します。ありがとうございます!

「いいね!」 1

どこでそれを見つけましたか?Sidekiqはメモリを節約するためにユニコーンマスター経由で起動されます。discourse_dockerにはこのコードは全く見当たりません。かなり古いセットアップを使用しているようですね。

「いいね!」 2

こんにちは。公式Dockerコンテナの実行時の事実に基づいて、これを厳密に再記述させてください。

実行中のコンテナで見られるもの(事実)

これはrunitを使用する公式Dockerインストールです(標準の/var/discourseランチャーワークフロー。インシデント直前の再ビルドなし)。コンテナ内では:

  1. runitのSidekiqサービスが存在し、監視されているのがそれです
ls -l /etc/service/sidekiq/run
sv status sidekiq

インシデント中の出力:

down: sidekiq: 1s, normally up, want up
  1. 手動でのSidekiq起動は成功します
cd /var/www/discourse
sudo -u discourse bundle exec sidekiq -C config/sidekiq.yml

これは起動したままで、Redisに接続し、ジョブを処理します。

  1. /etc/service/sidekiq/run のパッチ適用のみ(再ビルドなし)でクラッシュループが即座に修正されます。/etc/service/sidekiq/run を以下に置き換えました。
#!/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'

その後:

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

したがって、SidekiqはこのイメージではUnicornマスター経由で起動されておらず、実行スクリプトがクラッシュループするrunitサービスです。

discourse_docker内で正確なコードが見られない理由

同意します。厳密なテキストがリポジトリ内にないのは、/etc/service/sidekiq/runがイメージビルド/ブート中に生成/挿入される実行時の成果物であり、必ずしもdiscourse_docker内の逐語的なファイルではないからです。しかし、上記で示したように、これはこの公式イメージ内でアクティブに監視されているサービスです。

脆弱性を引き起こしたもの(事実+最小限の推論)

  • 標準のDebianパーミッションのため、logrotateの失敗も確認されました:/var/log = root:adm 0775であったため、logrotateはグローバルなsu root admを追加するまでローテーションを拒否しました。
  • logrotateが失敗したとき、/shared/log/rails/の下にsidekiq.logを含むファイルを再作成しました。
  • このイメージのデフォルトのrunitスクリプトはdiscourse:www-dataを使用し、/shared/logに強制的に-L log/sidekiq.logを指定していましたが、これによりSidekiqは共有ボリュームのパーミッションのずれに非常に敏感になり、有用なログが記録される前に即座に終了する可能性があります。

リクエスト/提案

上記を踏まえ、デフォルトのDocker/runit Sidekiqサービスを強化することを検討していただけないでしょうか?

提案されるデフォルト:

  • discourse:discourseとして実行する(コンテナ内の一般的な所有権と一致)。
  • bundle exec sidekiq -C config/sidekiq.yml経由で起動する。
  • 共有の-L log/sidekiq.logを強制するのを避ける(または耐障害性を持たせる)。

これにより、すべてのバックグラウンド/AIジョブを停止させるサイレントなdown: 1sのクラッシュループを防ぐことができます。

ご指定のブランチ/コミットがあれば、喜んでテストします。

もう一度… あなたがどこから画像を取得しているのか分かりません。

image

これが公式の画像です。

これは、公式の discourse docker で sidekiq という単語を検索した結果です。

3件のヒットがあり… runit ユニットに関するものはありません。これは unicorn 経由で管理されています。

「いいね!」 1

こんにちは。スクリーンショットはレイアウトを明確にするのに役立ちます。ありがとうございます。

現在の公式イメージでは、Sidekiq は独立した runit サービスではありません/etc/service/sidekiq/ がない)。これは、unicorn runit サービスの起動チェーンから起動されており、/etc/service のリストと一致しています。

私の報告は、スタンドアロンの runit ユニットに存在するか、unicorn/run の内部に存在するかに関わらず、その Sidekiq 起動パスのランタイム障害モードに関するものです。

私の VPS 上でのランタイムからの事実(公式 Docker、インシデント直前の再構築/アップグレードなし):

  1. バックグラウンドジョブが停止し、AI の応答が停止しました。

  2. Sidekiq は、コンテナのスーパーバイザー/起動チェーンによって起動されると、即座にクラッシュループに陥りました(ダウン:1秒)。

  3. bundle exec sidekiq -C config/sidekiq.yml として discourse 経由で手動で起動すると、稼働し続け、ジョブを処理したため、アプリ/redis は正常でした。

  4. 同時に、logrotate の失敗により /shared/log/rails/sidekiq.log(および関連パス)が異なるパーミッションで再作成されました。Sidekiq の起動コマンド(discourse:discourse として実行、sidekiq.yml を使用、shared を強制しない -L sidekiq.log)を安定させると、クラッシュループは即座に停止しました。

確かに、このイメージでは /etc/service/sidekiq/run というファイルは存在しないかもしれませんが、unicorn runit サービスに埋め込まれた Sidekiq の起動ステップは、共有ボリュームのパーミッション/logrotate のずれに対して脆弱であり、再構築なしで Sidekiq をサイレントに停止させる可能性があります。これが根本的な問題です。

提案: 公式の unicorn runit スクリプト(または生成される場所)で、Sidekiq の起動を強化することを検討してください。

  • discourse:discourse の下で Sidekiq を実行する
  • bundle exec sidekiq -C config/sidekiq.yml を優先する
  • 共有の -L log/sidekiq.log を強制するのを避ける(または耐障害性を持たせる)。