여러 컨테이너 인스턴스가 있는 도커 컨테이너에서 레일을 사용하여 마이그레이션 실행
저는 레일스용 도커 컨테이너를 만든 예를 많이 봤습니다.일반적으로 Rails 서버를 실행하고 마이그레이션/설정을 실행한 후 Rails 서버를 실행하는 CMD가 있습니다.
동시에 5개의 컨테이너를 생성하는 경우 마이그레이션을 시작하려는 여러 프로세스를 Rails가 어떻게 처리합니까?Rails가 일반 쿼리 로그(MySQL 데이터베이스)에서 현재 스키마 버전을 확인하는 것을 볼 수 있습니다.
SELECT `schema_migrations`.`version` FROM `schema_migrations`
하지만 다른 레일즈 사례에서 동시에 이런 일이 일어난다면 경주 상황을 알 수 있습니다.
DDL은 MySQL에서 트랜잭션이 아니며 마이그레이션을 실행하는 동안 일반 쿼리 로그에서 잠금이 발생하지 않는 것을 고려하면( 마이그레이션 단위 트랜잭션 이외에는) 병렬로 시작하는 것이 좋지 않은 것 같습니다.실제로 이 작업을 로컬에서 세 번 시작하면 테이블을 만들 때 두 개의 레일 인스턴스가 충돌하는 것을 볼 수 있습니다. 테이블이 이미 존재하기 때문에 세 번째 레일 인스턴스가 마이그레이션을 정상적으로 완료할 수 있습니다.데이터베이스에 무언가를 삽입한 마이그레이션이라면 매우 안전하지 않을 것입니다.
그렇다면 마이그레이션/설정을 실행한 다음(예: 여러 철도 작업자를 생성하는 유니콘 인스턴스를 생성하는) 단일 컨테이너를 실행하는 것이 더 나은 생각입니까?
N 레일 컨테이너와 마이그레이션을 실행하는 '마이그레이션 컨테이너' 하나를 생성한 후 종료해야 합니까?
더 좋은 방법이 있습니까?
특히 Rails에 대해서는 경험이 없지만, 도커와 소프트웨어 엔지니어링 관점에서 살펴보도록 하겠습니다.
Docker 팀은 컨테이너가 애플리케이션을 선적하는 것에 관한 것이라고 때로는 상당히 공격적으로 주장합니다.제롬 페타조니(Jerome Petazzoni)는 이 정말 훌륭한 성명서에서 이것은 모두 관심사 분리에 관한 것이라고 말합니다.저는 이것이 당신이 이미 알아낸 바로 그 점이라고 생각합니다.
마이그레이션이나 설정을 시작하는 레일 컨테이너를 실행하는 것이 초기 배포에 도움이 될 수 있으며 개발 중에도 종종 필요할 수 있습니다.하지만 생산에 들어갈 때는 고민을 분리하는 것을 정말로 고려해야 합니다.
따라서 Nrails 컨테이너를 실행하고 관리 작업을 수행하는 데 사용하는 도구/마이그먼트/설정 컨테이너를 추가하는 데 사용하는 이미지가 하나 있다고 생각합니다.공식 레일 이미지의 개발자들이 이에 대해 어떻게 이야기하는지 살펴봅니다.
폐기 컨테이너(소스 코드를 마운트하고 컨테이너를 시작하여 앱을 시작)로 사용할 수 있도록 설계되었으며, 다른 이미지를 구축할 수 있는 기반이 됩니다.
해당 이미지를 보면 설정 또는 마이그레이션 명령이 없습니다.그것을 어떻게 사용하느냐는 전적으로 사용자의 몫입니다.따라서 여러 개의 컨테이너를 실행해야 할 때는 계속 실행하십시오.
제가 mysql을 사용한 경험으로는 이것은 잘 작동합니다.데이터 전용 컨테이너를 실행하여 데이터를 호스팅하고, mysql 서버로 컨테이너를 실행한 후 마지막으로 백업 및 복원과 같은 관리 작업을 위한 컨테이너를 실행할 수 있습니다.세 개의 용기 모두 동일한 이미지를 사용할 수 있습니다.이제 워드프레스 컨테이너 몇 개에서 자유롭게 데이터베이스에 액세스할 수 있습니다.이는 명확한 관심사 분리를 의미합니다.사용할때docker-compose
그 모든 컨테이너를 관리하는 것은 그리 어렵지 않습니다.물론 이미 여러 개의 컨테이너로 구성된 복잡한 응용프로그램을 설정할 수 있도록 지원하는 많은 타사 컨테이너와 도구가 있습니다.
마지막으로 도커와 마이크로 서비스 아키텍처가 문제에 적합한지 결정해야 합니다.이 기사에 요약되어 있듯이 반대하는 몇 가지 이유가 있습니다.핵심적인 문제 중 하나는 그것이 전혀 새로운 복잡성 층을 추가한다는 것입니다.하지만 많은 해결책들이 그러하며, 여러분도 이 점을 알고 있고 기꺼이 이를 제외할 것이라고 생각합니다.
docker run <container name> rake db:migrate
하지만 CMD 를)를rails server
), 그러나rake db:migrate
업데이트: 로만이 제안한 명령은 다음과 같습니다.
docker exec <container> rake db:migrate
도커 스웜에 동일한 pb 퍼블리싱을 제공하여 다른 사람들로부터 부분적으로 얻은 해결책을 여기에 올렸습니다.
레일에는 이미 데이터베이스의 잠금을 사용하여 동시 마이그레이션을 탐지하는 메커니즘이 있습니다.그러나 대기만 해야 하는 동시 예외를 트리거합니다.
한 가지 해결책은 동시 예외가 발생할 때마다 5초만 기다렸다가 마이그레이션을 다시 실행하는 루프를 갖는 것입니다.마이그레이션이 실패하면 모든 컨테이너가 마이그레이션을 수행하는 것이 특히 중요합니다. 모든 컨테이너가 실패해야 합니다.
커피점퍼의 솔루션
namespace :db do
namespace :migrate do
desc 'Run db:migrate and monitor ActiveRecord::ConcurrentMigrationError errors'
task monitor_concurrent: :environment do
loop do
puts 'Invoking Migrations'
Rake::Task['db:migrate'].reenable
Rake::Task['db:migrate'].invoke
puts 'Migrations Successful'
break
rescue ActiveRecord::ConcurrentMigrationError
puts 'Migrations Sleeping 5'
sleep(5)
end
end
end
end
그리고 after_party, cron setup 등 마이그레이션을 수행하기 위해 하나씩 실행하고자 하는 다른 프로세스가 있는 경우도 있습니다.그런 다음 Rails와 동일한 메커니즘을 사용하여 데이터베이스 잠금 주위에 레이크 작업을 내장하는 것이 해결책입니다.
아래에서는 Rails 6 코드를 기반으로 migration_without_lock이 필요한 마이그레이션을 수행합니다.with_advisory_lock
데이터베이스 잠금(잠금을 얻을 수 없는 경우 동시 마이그레이션 오류 트리거)을 가져옵니다.
module Swarm
class Migration
def migrate
with_advisory_lock { migrate_without_lock }
end
private
def migrate_without_lock
**puts "Database migration"
Rake::Task['db:migrate'].invoke
puts "After_party migration"
Rake::Task['after_party:run'].invoke
...
puts "Migrations successful"**
end
def with_advisory_lock
lock_id = generate_migrator_advisory_lock_id
MyAdvisoryLockBase.establish_connection(ActiveRecord::Base.connection_config) unless MyAdvisoryLockBase.connected?
connection = MDAdvisoryLockBase.connection
got_lock = connection.get_advisory_lock(lock_id)
raise ActiveRecord::ConcurrentMigrationError unless got_lock
yield
ensure
if got_lock && !connection.release_advisory_lock(lock_id)
raise ActiveRecord::ConcurrentMigrationError.new(
ActiveRecord::ConcurrentMigrationError::RELEASE_LOCK_FAILED_MESSAGE
)
end
end
MIGRATOR_SALT = 1942351734
def generate_migrator_advisory_lock_id
db_name_hash = Zlib.crc32(ActiveRecord::Base.connection_config[:database])
MIGRATOR_SALT * db_name_hash
end
end
# based on rails 6.1 AdvisoryLockBase
class MyAdvisoryLockBase < ActiveRecord::AdvisoryLockBase # :nodoc:
self.connection_specification_name = "MDAdvisoryLockBase"
end
end
그러면 아까처럼 루프를 해서 기다리면 됩니다.
namespace :swarm do
desc 'Run migrations tasks after acquisition of lock on database'
task migrate: :environment do
result = 1
(1..10).each do |i|
**Swarm::Migration.new.migrate**
puts "Attempt #{i} sucessfully terminated"
result = 0
break
rescue ActiveRecord::ConcurrentMigrationError
seconds = rand(3..10)
puts "Attempt #{i} another migration is running => sleeping #{seconds}s"
sleep(seconds)
rescue => e
puts e
e.backtrace.each { |m| puts m }
break
end
exit(result)
end
end
그런 다음 시작 스크립트에서 레이크 작업을 시작합니다.
set -e
bundle exec rails swarm:migrate
exec bundle exec rails server -b "0.0.0.0"
마지막으로 마이그레이션 작업이 모든 컨테이너에 의해 실행되기 때문에 이미 완료된 상태에서 아무것도 할 수 없는 메커니즘이 있어야 합니다.(deb: migrate와 마찬가지로)
이 솔루션을 사용하면 스웜이 컨테이너를 런칭하는 순서는 더 이상 중요하지 않으며, 문제가 발생하면 모든 컨테이너가 문제를 알 수 있습니다 :-)
단일 컨테이너 ID의 경우:
docker exec -it <container ID> bundle exec rails db:migrate
multiple의 경우 다른 컨테이너에 대해 프로세스를 반복할 수 있습니다. 만약 1000개 안에 스크립트를 실행해야 하는 경우입니다.
언급URL : https://stackoverflow.com/questions/30842233/running-migrations-with-rails-in-a-docker-container-with-multiple-container-inst
'programing' 카테고리의 다른 글
Excel 선 그래프에서 셀 무시 (0) | 2023.10.14 |
---|---|
관리자 사용자를 프론트엔드 사용자와 동일한 테이블에 두는 것이 좋은 데이터베이스 설계입니까? (0) | 2023.10.14 |
자바스크립트는 1년 중 하루를 계산합니다 (1 - 366) (0) | 2023.10.09 |
CSS로 긴 문자열 잘라내기: 아직 실현 가능합니까? (0) | 2023.10.09 |
워드프레스 WXR로 디스크 XML 내보내기 주석을 가져오는 방법 (0) | 2023.10.09 |