Rails 本番・ステージング環境で危険タスクを禁止する方法(rails db:migrate:reset)

背景

rails db:migrate 実行時に誤ってrails db:migrate:reset が実行され本番環境のデータが復旧までの1時間ほど消えた状態となった。

rails db:migrate:reset は以下を行う非常に危険なコマンドです:

  1. データベース削除 (db:drop)
  2. 再作成 (db:create)
  3. マイグレーション適用 (db:migrate)
  4. 初期データ投入 (db:seed)

本番・ステージング環境で実行すると データが完全に消失 するため、誤操作防止が必須です。

→改善案:本番やステージング環境で不要な危険なコマンドを使用できなくする。

前提:

・環境変数で環境を判定

独自の環境変数を利用し、環境を判断できることが条件です。

Docker 環境では .env ファイルに独自の環境変数を設定し、Rails アプリ内で利用

# .env
APP_ENV=production   # 本番の場合
# APP_ENV=staging   # ステージングの場合
# APP_ENV=development # 開発の場合

Docker Compose から Rails アプリに渡るので、アプリ側では ENV[“APP_ENV”] で判定可能です

方法1: Rake タスクを上書きして禁止する

lib/tasks/disable_dangerous_tasks.rake を作成し、APP_ENV が production または staging の場合に危険タスクを無効化します。

# lib/tasks/disable_dangerous_tasks.rake

app_env = ENV["APP_ENV"]

if %w[production staging].include?(app_env)
  # migrate:reset を完全禁止
  Rake::Task["db:migrate:reset"].clear
  Rake::Task.define_task("db:migrate:reset") do
    abort "❌ ERROR: db:migrate:reset is disabled in #{app_env} environment!"
  end

  # 他の危険タスクも同様に禁止
  %w[db:drop db:schema:load db:setup].each do |task|
    if Rake::Task.task_defined?(task)
      Rake::Task[task].clear
      Rake::Task.define_task(task) do
        abort "❌ ERROR: #{task} is disabled in #{app_env} environment!"
      end
    end
  end
end

方法2: 環境設定ファイルでガードを追加(保険)

config/application.rb または config/environments/production.rb に保険として追加できます。

if defined?(Rake)
  if %w[production staging].include?(ENV["APP_ENV"])
    if Rake.application.top_level_tasks.grep(/db:(migrate:reset|drop|schema:load|setup)/).any?
      abort "❌ Forbidden task detected in #{ENV["APP_ENV"]} environment!"
    end
  end
end

方法3: Docker コンテナ側で alias 無効化

より安全にするなら、コンテナ内の .bashrc や .zshrc に alias を設定しておきます。

alias "rails db:migrate:reset"='echo "❌ ERROR: db:migrate:reset is disabled in this environment" && exit 1'
alias "rails db:drop"='echo "❌ ERROR: db:drop is disabled in this environment" && exit 1'

✅ 推奨構成

  • .env で APP_ENV を production / staging に設定
  • 方法1(タスク上書き)+ 方法2(保険ガード)で二重ロック
  • alias は運用側で必要に応じて適用

動作確認

# 本番環境
$ APP_ENV=production docker-compose exec oripa_api rails db:migrate:reset
❌ ERROR: db:migrate:reset is disabled in production environment!

# ステージング環境
$ APP_ENV=staging docker-compose exec oripa_api rails db:drop
❌ ERROR: db:drop is disabled in staging environment!

# 開発環境(従来通り実行可能)
$ APP_ENV=development docker-compose exec oripa_api rails db:migrate:reset
# => DBがリセットされる