mcpField
S
spree-deployment

spree-deployment

github.com/spreeクラウド・DevOps10223 回インストール4 週間前に更新
リポジトリ

spree-deployment について

spree-deploymentは、Spree 6を本番環境で運用するチーム向けのデプロイガイドです。コンテナ構築、環境設定、データベース、バックグラウンドジョブに加え、サービス分割の選択肢を説明します。

主な機能

  1. 01Dockerイメージ構築と配置
  2. 02一体型・分割ワーカー構成
  3. 03環境変数とデータベース設定
  4. 04マイグレーションとアップグレード
  5. 05Solid QueueとSidekiqの設定
  6. 06ダッシュボード・ストレージ・検索設定

活用シーン

  1. 01本番環境へのSpree 6ストアのデプロイ
  2. 02バックグラウンドワーカーのWebサービスからの分離
  3. 03データベース移行とバージョン更新の計画
  4. 04ダッシュボード配信とファイル保存の設定

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/spree/agent-skills --skill spree-deployment

インストール後、Claude Code で/spree-deploymentを入力して読み込まれたか確認します。

同梱ファイルreferences/environment-variables.md

詳細

Spree のデプロイ

Spree 6 の本番環境へのデプロイは、1 つの Docker イメージと 1 つのデータベースで構成されます。同じイメージで Store/Admin API を実行し、/dashboard で React 管理画面を配信し、デフォルトではバックグラウンドジョブもプロセス内で処理します。ジョブ(Solid Queue)とキャッシュ(Solid Cache)はデータベースに保存されます。Redis は任意です。すべて環境変数で設定します。

プロジェクト構成の確認:Rails アプリは server/(古いプロジェクトでは backend/)、管理画面は apps/dashboard/ にあります。Classic flavor(リポジトリのルートに Spree gem を配置したプレーンな Rails アプリ)では、同じ環境変数と Rails 設定を使用します。異なるのはパスと CLI ラッパーだけです。

トポロジーの選択

構成適用場面方法
統合型(デフォルト)ほとんどのストア。1 つのコンテナでアプリ全体を実行Web コンテナで Solid Queue プラグインを使って Puma を実行します(SOLID_QUEUE_IN_PUMA=true)
ワーカー分離型大規模なインポート、大規模カタログ、多数のセラーと同期するマーケットプレイス、マルチテナント同じイメージを使用し、bin/jobs コマンドで 2 つ目のサービスを起動します。Web 側では SOLID_QUEUE_IN_PUMA=false を設定します
分散型各サーフェスを個別にスケーリングまたはリリースする場合API はロードバランサーの背後に配置し、管理画面は CDN 上の静的ファイルとして配信します。ストアフロント(Next.js)は Vercel または Node ホストで実行します
Sidekiq専用の Solid Queue ワーカーで JOB_THREADS/JOB_CONCURRENCY を増やしても処理が追いつかなくなった場合のみActive Job アダプターを Redis/Valkey 上の Sidekiq に切り替えます(下記を参照)

まずは統合型から始めましょう。上位の構成への移行はすべて、コードではなく設定の変更で対応できます。

イメージのビルド

spree eject                                    # once — materializes server/ incl. its production Dockerfile
spree build --production                       # → <project>-spree:latest
spree build --production --tag registry.example.com/my-store:v42

# without the CLI, from the project root:
docker build . -f server/Dockerfile -t my-store

プロジェクトのルートをコンテキストとしてビルドします。Dockerfile は server/(Rails)と apps/dashboard/(カスタマイズした管理画面)を検出し、管理画面のビルド成果物をイメージに含めます。apps/dashboard/ がない場合は、標準の管理画面が含まれます。spree build --production は .spree-custom-app マーカー(初回リリースでは .spree-custom-dashboard)でレイアウト対応の Dockerfile を認識します。どちらのマーカーもない場合は、server/ をコンテキストとしてビルドします。そのため、server/Dockerfile がこのレイアウトへの対応以前のものであれば、spree-starter テンプレートから更新してください。更新しないと、カスタマイズした管理画面ではなく標準の管理画面がデプロイされます。

カスタマイズしていないストアでは、公式のマルチアーキテクチャイメージ ghcr.io/spree/spree:<version> をそのまま実行できます。

環境変数

必須:DATABASE_URL、SECRET_KEY_BASE。実質的に必須:RAILS_HOST と 3 つの Active Record 暗号化キー。全項目の一覧:references/environment-variables.md。

変数重要な理由
DATABASE_URLpostgres://…、mysql2://…、または sqlite3:db/production.sqlite3
SECRET_KEY_BASEopenssl rand -hex 64。値を変更しないでください。セッション、Cookie、JWT のフォールバック、シークレット API キーのダイジェストはこの値に依存します
ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY、ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY、ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALTWebhook 署名シークレット、ゲートウェイの顧客 ID、OAuth ID トークンを保存時に暗号化します。未設定の場合は平文で保存され、起動時に警告が表示されます。spree encryption init --print または bin/rails db:encryption:init で生成してください。環境ごとに別の値を設定し、バックアップを取ってください。データを暗号化した後は絶対に変更しないでください。ワーカーにも Web と同じ値が必要です。spree-security を参照してください
RAILS_HOSTメールリンク、Webhook ペイロード、API レスポンスの添付ファイル URL に使う公開ホスト(プロトコルなし)。未設定の場合、URL は localhost を指します。Render では RENDER_EXTERNAL_HOSTNAME が代わりに使われます
CDN_HOSTアセットと画像用の任意の別ホスト(config.asset_host と Spree.cdn_host を設定します)
SOLID_QUEUE_IN_PUMAtrue(デフォルト)の場合は Web プロセスでジョブを実行します。bin/jobs ワーカーを実行する場合は false に設定します
JOB_THREADS / JOB_CONCURRENCYワーカースレッド数(デフォルト 3)/ワーカープロセス数(デフォルト 1)
SPREE_IMPORT_JOB_CONCURRENCY同時に実行する CSV インポートのグループジョブ数の上限(デフォルトは JOB_THREADS の 75%、最小 1。0 = 上限なし)
MISSION_CONTROL_USER / MISSION_CONTROL_PASSWORD/jobs の HTTP Basic 認証。production で未設定の場合、ダッシュボードはロックされます
SMTP_HOST(および SMTP_PORT、SMTP_USERNAME、SMTP_PASSWORD、SMTP_FROM_ADDRESS)SMTP_HOST がない場合、production ではメールを送信しません。インストール不要で、任意の SMTP プロバイダーを使用できます。STARTTLS は常に試行されるため、暗黙的な TLS の 465 ではなくポート 587 を使用してください。認証方式は plain で、SMTP_USERNAME が設定されている場合にのみ送信されます。マーチャントは「Settings → Emails」で、ストアごとに From/Reply-To を上書きできます
AWS_* または CLOUDFLARE_*アップロード用のオブジェクトストレージ。エフェメラルファイルシステムでは必須です
RAILS_FORCE_SSL / RAILS_ASSUME_SSLどちらもデフォルトは true(TLS 終端プロキシの背後で使用)。TLS が一切ない場合に限り、両方を false に設定します
SENTRY_DSN、OTEL_*、MEILISEARCH_URL任意の連携機能(以下を参照)

Puma:PORT(3000)、RAILS_MAX_THREADS(3)、WEB_CONCURRENCY(1。auto = コアごとに 1 つ、約 1 GB の RAM)。

データベース

  • PostgreSQL を推奨します。MySQL/MariaDB と SQLite も production でサポートされています(SQLite は単一ホストのみ、小規模なストア向けです)。
  • コネクションプールのサイズは、Web スレッド と プロセス内ジョブスレッドの合計をカバーする必要があります。テンプレートの database.yml では RAILS_MAX_THREADS + JOB_THREADS + 3 を使用します。database.yml を手動で作成する場合も、この計算式を維持してください。維持しないと、Puma 内のジョブがプールを使い切ります。
  • Solid Queue、Solid Cache、Solid Cable のテーブルは同じデータベースにあります。まとめてバックアップしてください。

マイグレーション、シード、アップグレード

  • イメージのエントリーポイント(bin/docker-entrypoint)は、コンテナーのコマンドが Rails サーバーの場合に限り、bin/rails db:prepare を実行します。初回起動時は作成とシードを行い、以降の起動時はマイグレーションを行います。bin/jobs のワーカーはマイグレーションを行いません。
  • 初回起動時: スケールアウトする前に、リリース/初期化ジョブで bin/rails db:prepare を実行するか、Web レプリカを 1 つだけ起動して、一度だけ実行してください。Rails のマイグレーションアドバイザリロック(PostgreSQL/MySQL)が直列化するのはマイグレーションのみであり、データベースの作成、スキーマのロード、シード処理は対象外です。そのため、空のデータベースで複数のレプリカが同時に実行されると、競合や二重シードが発生する可能性があります。
  • 以降の起動時: PostgreSQL/MySQL では、複数の Web レプリカが同時にマイグレーションを実行しても問題ありません(アドバイザリロックが機能します)。プラットフォームにリリース/デプロイ前フェーズがあり、マイグレーション失敗時にロールアウトを中止したい場合は、代わりにそこで bin/rails db:migrate を実行してください。
  • Spree gem のアップグレード後は、環境ごとに一度、データのバックフィルを実行してください:bin/rails spree:upgrade(冪等です。プレビューするには DRY_RUN=1 を指定します)。エントリーポイントでは実行されないため、リリースコマンドまたは単発タスクを使用してください。spree-upgrade スキルを参照してください。
  • デフォルトの管理者は存在しません。シード処理では一度限りのセットアップリンクが表示されます。bin/rails spree:setup:token で再表示できます。

バックグラウンドジョブ(Solid Queue)

  • config/puma.rb は、SOLID_QUEUE_IN_PUMA が true の場合に plugin :solid_queue を読み込みます。

  • config/queue.yml は、ワーカーキューをポーリング順(チェックアウトの重要キュー、次にカタログ一括処理、最後に Active Storage のハウスキーピング)で定義します。末尾に "*" を指定しているため、リストにないキューも実行されます。

  • Spree ジョブは Spree.queues.<name> からキューを選択します(すべてのエントリーのデフォルトは :default です)。テンプレートの config/initializers/spree.rb で、これらを spree_* の名前にマッピングしています。調整については spree-performance を参照してください。

  • config/recurring.yml は Solid Queue の cron 設定です。テンプレートでは次のジョブをスケジュールしています(本番環境のみ)。

    • Spree::StockReservations::ExpireJob — 毎分(期限切れのチェックアウト保留を解除します)
    • Spree::Orders::FinalizeStaleDraftsJob — 5 分ごと(決済が成功したものの完了処理がコミットされなかった注文を完了します。返金は行いません)
    • Spree::Carts::ReapExpiredJob — 毎時(有効期限を過ぎた放棄カートを削除します。有効な決済があるカートは保持されます)
    • Spree::Collections::RegenerateTimeBasedJob — 毎時(時間ベースのルールを持つ自動コレクションを処理します)
    • spree:price_history:prune rake タスク(command: エントリー経由)— 毎日(店舗ごとの EU オムニバス指令のデータ保持処理)
    • Spree::SellerPayouts::SweepDueJob — 毎日(マーケットプレイスの支払い処理。売り手への支払い間隔にかかわらず、毎日実行してください。支払い期日は売り手ごとに判定されます)
    • Spree::SellerTransfers::ExecutePendingDueJob — 毎時(拒否された売り手への送金を再試行します)
    • Solid Queue の完了済みジョブと Solid Cable メッセージのクリーンアップ — 毎時

    マーケットプレイスのジョブは、売り手のいないストアでは何も行いませんが、残しておいてください。プロジェクトがいずれかのエントリーより前のものなら、spree-starter の config/recurring.yml からコピーしてください。

  • 分割モード:同じ環境変数(DATABASE_URL、SECRET_KEY_BASE、RAILS_HOST)を使って bin/jobs を実行するサービスを追加し、そこで JOB_THREADS を増やしてください。JOB_CONCURRENCY またはレプリカ数を増やしてスケールし、Web では SOLID_QUEUE_IN_PUMA=false を設定します。分割デプロイでは、インポートをエンキューするプロセスで上限が計算されるため、Web で SPREE_IMPORT_JOB_CONCURRENCY を明示的に設定してください。

Mission Control(/jobs)

テンプレートのルートで /jobs にマウントされています。MISSION_CONTROL_USER と MISSION_CONTROL_PASSWORD を設定してください。開発環境でのデフォルトは spree / spree123 です。Solid Queue 専用です。

Sidekiq への切り替え(任意)
# Gemfile
gem "sidekiq"
gem "sidekiq-cron"        # replaces config/recurring.yml

# config/application.rb
config.active_job.queue_adapter = :sidekiq
  • config/sidekiq.yml には、Spree.queues で割り当てたすべてのキューに加えて、default、mailers、active_storage_* キューを指定する必要があります。Sidekiq には "*" のような全キューを対象とする設定がないため、指定されていないキューは実行されません。
  • recurring.yml のスケジュールを config/schedule.yml(sidekiq-cron)に移してください。sidekiq-cron がスケジュールするのはコマンドではなくジョブなので、spree:price_history:prune タスクを小さなジョブでラップしてください(定義されていない場合は Rails.application.load_tasks を実行し、その後 Rake::Task[...].tap(&:reenable).invoke を呼び出します)。
  • Mission Control の代わりに Sidekiq::Web を認証の背後にマウントしてください。独立したサービスとして bundle exec sidekiq を実行し、RAILS_MAX_THREADS >= SIDEKIQ_CONCURRENCY に設定してください。SOLID_QUEUE_IN_PUMA は削除してください。

キャッシュ

Solid Cache(config.cache_store = :solid_cache_store)がデフォルトなので、プロビジョニングは不要です。インメモリーキャッシュを使う場合は、redis gem を追加し、config/environments/production.rb で config.cache_store = :redis_cache_store, { url: ENV["REDIS_URL"] } を設定してください。Valkey でも動作します。

管理ダッシュボード

  • 単一ノード(デフォルト): spree_dashboard gem は、SPA フォールバック付きで /dashboard にビルド済みダッシュボードを(任意でマーケットプレイスの出品者パネルを /sellers に)配信します。ディレクトリは Spree::Dashboard.dist_path または SPREE_DASHBOARD_DIST_PATH から配信されます(出品者パネル:SPREE_SELLER_PANEL_DIST_PATH)。未設定の場合は 404 になります。イメージのビルドで設定されるため、同一オリジンで CORS も API キーも不要です。
  • 静的ホスト/CDN: cd apps/dashboard && VITE_SPREE_API_URL=https://api.example.com pnpm build を実行して dist/ をアップロードし、/* → /index.html にリライトしてください。両側で HTTPS を有効にしてください(リフレッシュ Cookie は SameSite=None; Secure です)。ダッシュボードのオリジンを Settings → Allowed Origins に追加してください。VITE_* の値はバンドルに埋め込まれるため、ここにシークレットを設定しないでください。環境ごとにビルドが必要です。
  • API 専用のデプロイでは、spree_dashboard gem を削除できます。

ファイルストレージと CDN

  • アップロードには Active Storage を使用します。デフォルトのローカルディスクは、一時的なプラットフォーム(Render、Heroku、Fargate、ほとんどの K8s)ではデータが失われます。AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY(および AWS_REGION、AWS_BUCKET)、または CLOUDFLARE_ENDPOINT と CLOUDFLARE_ACCESS_KEY_ID/CLOUDFLARE_SECRET_ACCESS_KEY(および CLOUDFLARE_BUCKET)を設定してください。テンプレートがサービスを自動検出します。その他のバックエンドは標準の config/storage.yml で設定します。
  • /rails/active_storage/representations/ と /assets の前段に CDN を配置してください。CDN のホスト名が別の場合は、CDN_HOST を設定してください。

検索

デフォルトの検索には SQL(Spree::SearchProvider::Database)を使用します。大規模なカタログでは Meilisearch を追加してください。

# Gemfile
gem "spree_meilisearch"
# config/initializers/spree.rb
Spree.search_provider = 'SpreeMeilisearch::SearchProvider'

環境変数:MEILISEARCH_URL(デフォルトは http://localhost:7700)、MEILISEARCH_API_KEY。初回デプロイ後とプロバイダー設定の変更後にインデックスを構築してください:bin/rails spree:search:reindex(ローカルでは spree task search:reindex)。

可観測性

  • エラー: SENTRY_DSN を設定してください(テンプレートには sentry-rails が含まれています)。
  • トレース: gem 'spree_opentelemetry' を追加し、標準の OTel 変数(OTEL_SERVICE_NAME、OTEL_EXPORTER_OTLP_ENDPOINT または OTEL_EXPORTER_OTLP_TRACES_ENDPOINT、任意で OTEL_TRACES_SAMPLER=parentbased_traceidratio と OTEL_TRACES_SAMPLER_ARG)を設定してください。エクスポーター変数が設定されるまで動作せず、OTEL_SDK_DISABLED=true が常に優先されます。HTTP、SQL、ジョブ、すべての Spree::Workflow の実行/ステップ/フック、イベントのディスパッチ、Webhook の配信(W3C トレースコンテキストを伝播)、ゲートウェイ呼び出しを対象にスパンを記録します。スパン属性に個人識別情報(PII)は含まれません。
  • Sentry + OTel: エラーには Sentry を使い、Sentry の traces_sample_rate も設定することは避けてください(計測が二重になります)。Sentry をトレースのバックエンドとして使うには、可観測性ドキュメントの Sentry のセクションを参照してください(sentry-opentelemetry、Sentry.init の前に SpreeOpenTelemetry.install! を実行、OTEL_TRACES_EXPORTER=none)。
  • ヘルスチェック: /up(Rails 組み込みの機能です。DB にはアクセスしません)。

プラットフォームに関する注意事項

  • Render: create-spree-app はスターターの Blueprint をプロジェクトルートに render.yaml として配置し、すべてのサービスの dockerfilePath(コメントアウトされたワーカーを含む)を ./server/Dockerfile に設定し、リポジトリのルートを dockerContext に指定します。Web サービス 1 つと Postgres の構成です。Blueprint は SECRET_KEY_BASE、3 つの ACTIVE_RECORD_ENCRYPTION_* キー(Environment タブからシークレットマネージャーにコピーしてください)、および MISSION_CONTROL_PASSWORD(ユーザー名は jobs)を生成します。分割モードにするには、ワーカーブロックのコメントを解除してください。ファイルシステムは一時的なものです。S3/R2 を設定してください。
  • AWS: EC2 と RDS をそれぞれ 1 つずつ使用するか、ECS Fargate を使用します(同じイメージから Web と bin/jobs を別々のサービスとして実行します)。
  • Kubernetes: Web 用の Deployment を 1 つ作成し、必要に応じて bin/jobs 用も作成します。SECRET_KEY_BASE/DATABASE_URL/ストレージ認証情報には Secrets を使用します。/up でライブネスを確認します。マイグレーションはエントリーポイント(サーバーコマンド)で実行するか、db:migrate を実行する Job/init コンテナで実行します。
  • VPS: docker compose で web と postgres を構成すれば、完全なデプロイになります。TLS 用に Caddy/nginx/Traefik を追加してください。

よくある問題

症状原因/対処方法
画像/添付ファイルの URL が https://localhost/... を指しているRAILS_HOST(または CDN_HOST)が未設定です
/dashboard が 404 を返すspree_dashboard gem がないか、SPREE_DASHBOARD_DIST_PATH/Spree::Dashboard.dist_path が未設定です(ダッシュボードのステージなしでカスタムイメージをビルドしています)
静的ホストのダッシュボードにログインできない/CORS エラーが発生するOrigin が Settings → Allowed Origins に含まれていないか、両方が HTTPS ではありません
/jobs が常に 401 を返す本番環境で MISSION_CONTROL_USER/PASSWORD が未設定です
分割後にジョブがまったく実行されないワーカーが bin/jobs を実行していないか、ワーカーがない状態で Web に SOLID_QUEUE_IN_PUMA=false が設定されています
Solid Queue ではジョブが実行されるが、Sidekiq では実行されないsidekiq.yml にキューがありません(すべてを受け取る設定がありません)
負荷時に ActiveRecord::ConnectionTimeoutError が発生するプールサイズが RAILS_MAX_THREADS + JOB_THREADS より小さいです
再デプロイ後にアップロードしたファイルが消える一時的なファイルシステムでローカルディスクストレージを使用しています
本番環境でメールが届かないSMTP_HOST が未設定です
Meilisearch で検索しても何も返らないインデックスがまだ作成されていません。spree:search:reindex を実行してください
チェックアウト時の在庫確保が解除されない/販売者に支払いが行われないrecurring.yml のエントリーがないか、production: キーの下にないか、Sidekiq に移行した際にそれらを移行していません
gem の更新後、ストアデータのマイグレーションが途中までに見えるspree:upgrade を実行していません
起動ログに [Spree] Active Record encryption is not fully configured と表示されるACTIVE_RECORD_ENCRYPTION_* が未設定(または config.active_record.encryption に読み込まれていません)。シークレットが平文で保存されています
デプロイ後に Webhook のシークレット/ゲートウェイの顧客情報を読み取れないこの環境で暗号化キーが変更されたか、設定されていません。または、既存の行を暗号化する前に暗号化を有効にしました(spree-security を参照)

さらに読むには

  • node_modules/@spree/docs/dist/developer/deployment/ — quickstart.md、docker.md、environment_variables.md、background_jobs.md、caching.md、database.md、assets.md、cdn.md、emails.md、render.md、aws.md、aws_ecs.md
  • node_modules/@spree/docs/dist/developer/dashboard/deployment.md
  • node_modules/@spree/docs/dist/developer/providers/observability.md
  • https://spreecommerce.org/docs/developer/deployment/quickstart
  • 関連スキル:spree-performance(キューとワーカーのチューニング)、spree-upgrade(リリースフェーズでのアップグレード)、spree-security(シークレット、暗号化キー)、spree-multi-tenant(ルートドメイン、Cookie、ホスト)

コメント