Backup 젬과 Whenever 젬을 이용한 서버 백업 자동화

레일스 프로젝트를 원격 서버로 배포후 제대로 동작하는 것이 확인되면 우선적으로 데이터베이스와 사용자들이 업로드한 파일들을 주기적으로 백업해야 한다.

여러가지 방법이 있지만 이 글에서는 가장 흔히들 사용하는 backup 젬과, crontab 스크립트를 편리하게 작업할 수 있게 해 주는, whenever 젬을 이용하는 방법에 대해서 필자의 짧은 경험과 함께 소개할 것이다.

(1) Backup 젬

backup 젬은 프로젝트의 gemfile로 설치하지 않고 직접 원격 서버에서 설치한다. 물론 레일스 애플리케이션이 서비스되는 서버에는 이미 루비환경이 설치되어 있게 때문에 ssh로 접속한 후 아래와 같은 명령으로 간단하게 설치할 수 있다.

$ gem install backup

이제 커맨드라인에서 backup 명령을 실행하면 아래와 같이 사용법에 대한 도움말을 볼 수 있다.

$ backup
Commands:
  backup check                                      # Check for configuration errors or warnings
  backup generate:config                            # Generates the main Backup configuration file
  backup generate:model -t, --trigger=TRIGGER       # Generates a Backup model file.
  backup help [COMMAND]                             # Describe available commands or one specific command
  backup perform -t, --triggers, --trigger=TRIGGER  # Performs the backup for the specified trigger(s).
  backup version                                    # Display installed Backup version

backup에서는 model이라는 용어를 사용하여 하나의 백업 작업을 정의한다. 따라서 위의 5번째 라인의 명령과 몇가지 옵션을 추가해서 아래와 같이 백업작업을 정의한다.

$ backup generate:model --trigger=daily_db_backup --databases='mysql' --storages='local' --compressor='gzip' --notifiers='mail'
Generated configuration file: '/home/[user_account]/Backup/config.rb'.
Generated model file: '/home/[user_account]/Backup/models/daily_db_backup.rb'.

옵션설명

--trigger : 모델명을 지정한다.

--databases : 데이터베이스명을 지정한다. ex) MySQL, PostgreSQL, MongoDB, Redis, Riak, SQLite

--storages : 백업저장 위치를 지정한다. ex) Amazon S3, Rackspace Cloud Files, Ninefold, Dropbox, FTP, SFTP, SCP, RSync, Local

--compressor : 데이터 압축프로그램을 지정한다. ex) Gzip, Bzip2, xz

--notifiers : 백업작업 완료시 결과를 알려주는 툴을 지정한다. ex) Email (SMTP, Sendmail, Exim and File delivery), Twitter, Campfire, Prowl, Hipchat, Pushover, Nagios, HTTP POST (compatible with a variety of services), Zabbix

위의 명령 실행결과 Backup이라는 폴더에 아래와 같은 파일들이 생성된다.

$ tree Backup
Backup
├── config.rb
└── models
    └── daily_db_backup.rb

1 directory, 2 files

모델 파일(daily_db_backup.rb)을 열고 자신의 서버 환경에 맞게 옵션을 변경한다.

# encoding: utf-8

##
# Backup Generated: daily_db_backup
# Once configured, you can run the backup with the following command:
#
# $ backup perform -t daily_db_backup [-c <path_to_configuration_file>]
#
# For more information about Backup's components, see the documentation at:
# http://backup.github.io/backup
#
Model.new(:daily_db_backup, 'Description for daily_db_backup') do

  ##
  # MySQL [Database]
  #
  database MySQL do |db|
    # To dump all databases, set `db.name = :all` (or leave blank)
    db.name               = "my_database_name"
    db.username           = "my_username"
    db.password           = "my_password"
    db.host               = "localhost"
    db.port               = 3306
    # db.socket             = "/tmp/mysql.sock"
    # Note: when using `skip_tables` with the `db.name = :all` option,
    # table names should be prefixed with a database name.
    # e.g. ["db_name.table_to_skip", ...]
    # db.skip_tables        = ["skip", "these", "tables"]
    # db.only_tables        = ["only", "these", "tables"]
    db.additional_options = ["--quick", "--single-transaction"]
  end

  ##
  # Local (Copy) [Storage]
  #
  store_with Local do |local|
    local.path       = "~/backups/"
    local.keep       = 5
  end

  ##
  # Gzip [Compressor]
  #
  compress_with Gzip

  ##
  # Mail [Notifier]
  #
  # The default delivery method for Mail Notifiers is 'SMTP'.
  # See the documentation for other delivery options.
  #
  notify_by Mail do |mail|
    mail.on_success           = true
    mail.on_warning           = true
    mail.on_failure           = true

    mail.from                 = "sender@email.com"
    mail.to                   = "receiver@email.com"
    mail.address              = "smtp.gmail.com"
    mail.port                 = 587
    mail.domain               = "your.host.name"
    mail.user_name            = "sender@email.com"
    mail.password             = "my_password"
    mail.authentication       = "plain"
    mail.encryption           = :starttls
  end

end

위의 소스 코드에서 19번 라인의 db.name 속성값은 18번 라인의 코멘트와 같이 :all을 지정하여 모든 데이터베이스를 한꺼번에 백업할 수도 있다. 여기서는 불필요한 24번 코드라인을 코멘트 처리하였고, 28, 29번 코드라인은 전체 데이터베이스를 백업할 것이기 때문에 사용할 필요가 없어서 코멘트 처리하였다.

여기서는 project1_production이라는 데이터베이스를 백업하는 것으로 가정한다. 따라서 db.name 값을 project1_production으로 지정한다.

위에서 설명한 바와 같이 모델을 작성하는 커맨드라인을 다시 한번 상기해 보자.

$ backup generate:model --trigger=daily_db_backup --databases='mysql' --storages='local' --compressor='gzip' --notifiers='mail'

--storages='local'은 백업을 저장할 매체를 지정하는 것인데, 여기서는 local, 즉, 동일한 서버에 저장하겠다는 의미이다. 언급한 바와 같이 scp를 지정하여 다른 서버로 백업본을 저장할 수도 있다.

--nofifiers='mail'은 백업 결과를 알려주는 매체를 지정한다. mail로 지정하면, 백업 결과를 이메일로 발송해 준다.

리눅스 서버의 sendmail 기능을 이용하면 구글 SMTP 서비스를 사용하지 않아도 된다. 위의 모델 파일에서 59~65 코드라인을 코멘트 처리하고 아래의 코드라인을 추가한다.

mail.delivery_method = :sendmail

이제부터는 백업이 실행되면 서버의 sendmail 기능을 이용하여 메일을 발송하게 된다.

지금까지 변경한 내용을 반영한 모델 파일의 내용은 아래와 같다.

# encoding: utf-8

##
# Backup Generated: daily_db_backup
# Once configured, you can run the backup with the following command:
#
# $ backup perform -t daily_db_backup [-c <path_to_configuration_file>]
#
# For more information about Backup's components, see the documentation at:
# http://backup.github.io/backup
#
Model.new(:daily_db_backup, 'backups project1_production database') do

  ##
  # MySQL [Database]
  #
  database MySQL do |db|
    # To dump all databases, set `db.name = :all` (or leave blank)
    db.name               = "project1_production"
    db.username           = "[account-name]"
    db.password           = "[account-pass]"
    db.host               = "localhost"
    db.port               = 3306

    db.additional_options = ["--quick", "--single-transaction"]
  end

  ##
  # Local (Copy) [Storage]
  #
  store_with Local do |local|
    local.path       = "~/backups/"
    local.keep       = 5
  end

  ##
  # Gzip [Compressor]
  #
  compress_with Gzip

  ##
  # Mail [Notifier]
  #
  # The default delivery method for Mail Notifiers is 'SMTP'.
  # See the documentation for other delivery options.
  #
  notify_by Mail do |mail|
    mail.on_success           = true
    mail.on_warning           = true
    mail.on_failure           = true

    mail.delivery_method      = :sendmail
    mail.from                 = "sender@email.com"
    mail.to                   = "receiver@email.com"

  end

end

자, 이제, 서버의 커맨드라인에서 아래와 같이 백업 명령을 실행해 보자.

$ backup perform -t daily_db_backup

제대로 동작했다면 서버의 /home/[account-name]/backups/ 폴더에 백업 파일이 저장되고 결과가 메일로 발송되었을 것이다. 추가로 백업 명령의 로그를 저장하고 싶을 때는 아래와 같이 옵션을 추가할 수 있다.

$ backup perform -t daily_db_backup >> /home/[user-account]/Backup/config/cron.log 2>&1

(2) Whenever 젬

다음은 마지막으로 실행한 명령을 시스템 cron job으로 등록해 보도록 하자. 여기서는 cron job 설정을 편리하게 하기 위해서 whenever 젬을 사용하기로 한다. 이를 위해서 서버로 접속한 후 아래와 같이 whenever 젬을 시스템에 설치한다.

$ gem install whenever

그리고 Backup 디렉토리로 이동한 후 config 디렉토리를 생성한다. whenever 젬에서 필요로로 하는 파일을 생성하기 위해서 아래와 같이 명령을 수행한다.

$ wheneverize .

위의 명령을 실행하여 생성된 config/schedule.rb 파일을 열고 아래와 같이 수정한다.

every 1.day, :at => '12:00 am' do
  command "backup perform -t daily_db_backup >> /home/[user-account]/Backup/config/cron.log 2>&1"
end

즉, 매일 자정에 백업 작업을 실행하라는 루비 코드를 작성한 것이다.

이제 마지막으로 해야할 일은, 이 스케쥴파일을 실제로 crontab에 반영하기 위해서 crobtab을 업데이트하는 것이다.

$ whenever --update-crontab

그리고 crontab -l 명령으로 제대로 반영되었는지를 확인한다.

(3) 요약

지금까지 설명한 내용을 요약하면, 데이터베이스 덤프파일을 작성하여 압축한 후 특정 매체에 저장하도록 하였고 이 작업을 cron job으로 등록하여 데이터베이스의 자동 백업을 기능을 구현해 보았다.

참고문서

1. Backup a Rails Database With the Backup and Whenever Gems
2. Scheduled Backups with Clockwork and Backup Gem
3. Schedule Cron Jobs with the Whenever Gem